Spec-Zone.ru › Phalcon 3

Phalcon Query Language (PHQL)

Phalcon Query Language, PhalconQL или просто PHQL — это высокоуровневый, объектно-ориентированный диалект SQL, который позволяет писать запросы, используя стандартизированный язык, похожий на SQL. PHQL реализован как парсер (написанный на C), который преобразует синтаксис в синтаксис целевой СУБД.

Для достижения максимальной производительности Phalcon предоставляет парсер, который использует ту же технологию, что и SQLite. Эта технология обеспечивает небольшой парсер в оперативной памяти с очень низким потреблением памяти, который также является потокобезопасным.

Парсер сначала проверяет синтаксис переданного оператора PHQL, затем строит промежуточное представление оператора и, наконец, преобразует его в соответствующий диалект SQL целевой СУБД.

В PHQL реализован ряд функций, которые повышают безопасность доступа к базам данных:

  • Связанные параметры являются частью языка PHQL, помогая защитить ваш код.
  • PHQL позволяет выполнять только один оператор SQL за вызов, предотвращая инъекции.
  • PHQL игнорирует все комментарии SQL, которые часто используются при SQL-инъекциях.
  • PHQL позволяет только операторам манипулирования данными, избегая случайного или внешнего изменения или удаления таблиц/баз данных без авторизации.
  • PHQL реализует высокоуровневую абстракцию, позволяющую обрабатывать таблицы как модели, а поля — как атрибуты класса.

Пример использования

Для лучшего объяснения работы PHQL рассмотрим следующий пример. У нас есть две модели «Cars» и «Brands»:

use Phalcon\Mvc\Model;

class Cars extends Model
{
    public $id;

    public $name;

    public $brand_id;

    public $price;

    public $year;

    public $style;

    /**
     * This model is mapped to the table sample_cars
     */
    public function getSource()
    {
        return "sample_cars";
    }

    /**
     * A car only has a Brand, but a Brand have many Cars
     */
    public function initialize()
    {
        $this->belongsTo("brand_id", "Brands", "id");
    }
}

И каждый автомобиль имеет бренд, поэтому бренд имеет много автомобилей:

use Phalcon\Mvc\Model;

class Brands extends Model
{
    public $id;

    public $name;

    /**
     * The model Brands is mapped to the "sample_brands" table
     */
    public function getSource()
    {
        return "sample_brands";
    }

    /**
     * A Brand can have many Cars
     */
    public function initialize()
    {
        $this->hasMany("id", "Cars", "brand_id");
    }
}

Создание запросов PHQL

Запросы PHQL можно создавать, просто создав экземпляр класса Phalcon\Mvc\Model\Query:

use Phalcon\Mvc\Model\Query;

// Instantiate the Query
$query = new Query(
    "SELECT * FROM Cars",
    $this->getDI()
);

// Execute the query returning a result if any
$cars = $query->execute();

Из контроллера или представления их легко создать/выполнить, используя инжектированный менеджер моделей:

// Executing a simple query
$query = $this->modelsManager->createQuery("SELECT * FROM Cars");
$cars  = $query->execute();

// With bound parameters
$query = $this->modelsManager->createQuery("SELECT * FROM Cars WHERE name = :name:");
$cars  = $query->execute(
    [
        "name" => "Audi",
    ]
);

Или просто выполнить его:

// Executing a simple query
$cars = $this->modelsManager->executeQuery(
    "SELECT * FROM Cars"
);

// Executing with bound parameters
$cars = $this->modelsManager->executeQuery(
    "SELECT * FROM Cars WHERE name = :name:",
    [
        "name" => "Audi",
    ]
);

Выбор записей

Как и знакомый SQL, PHQL позволяет запросить записи, используя оператор SELECT, который нам знаком, за исключением того, что вместо указания таблиц мы используем классы моделей:

$query = $manager->createQuery(
    "SELECT * FROM Cars ORDER BY Cars.name"
);

$query = $manager->createQuery(
    "SELECT Cars.name FROM Cars ORDER BY Cars.name"
);

Также разрешены классы в пространствах имен:

$phql  = "SELECT * FROM Formula\Cars ORDER BY Formula\Cars.name";
$query = $manager->createQuery($phql);

$phql  = "SELECT Formula\Cars.name FROM Formula\Cars ORDER BY Formula\Cars.name";
$query = $manager->createQuery($phql);

$phql  = "SELECT c.name FROM Formula\Cars c ORDER BY c.name";
$query = $manager->createQuery($phql);

PHQL поддерживает большую часть стандартного SQL, включая нестандартные директивы, такие как LIMIT:

$phql = "SELECT c.name FROM Cars AS c WHERE c.brand_id = 21 ORDER BY c.name LIMIT 100";

$query = $manager->createQuery($phql);

Типы результатов

В зависимости от типа столбцов, которые мы запрашиваем, тип результата будет различаться. Если вы получаете один целый объект, то возвращаемый объект — Phalcon\Mvc\Model\Resultset\Simple. Такой результат — набор полных объектов модели:

$phql = "SELECT c.* FROM Cars AS c ORDER BY c.name";

$cars = $manager->executeQuery($phql);

foreach ($cars as $car) {
    echo "Name: ", $car->name, "\n";
}

Это точно так же, как:

$cars = Cars::find(
    [
        "order" => "name"
    ]
);

foreach ($cars as $car) {
    echo "Name: ", $car->name, "\n";
}

Полные объекты можно изменять и повторно сохранять в базе данных, потому что они представляют собой полную запись связанной таблицы. Есть и другие типы запросов, которые не возвращают полные объекты, например:

$phql = "SELECT c.id, c.name FROM Cars AS c ORDER BY c.name";

$cars = $manager->executeQuery($phql);

foreach ($cars as $car) {
    echo "Name: ", $car->name, "\n";
}

Мы запрашиваем только некоторые поля в таблице, поэтому их нельзя рассматривать как целый объект, поэтому возвращаемый объект по-прежнему является набором результатов типа Phalcon\Mvc\Model\Resultset\Simple. Однако каждый элемент — это стандартный объект, содержащий только два запрошенных столбца.

Эти значения, которые не представляют полные объекты, мы называем скалярами. PHQL позволяет запросить все типы скаляров: поля, функции, литералы, выражения и т. д.:

$phql = "SELECT CONCAT(c.id, ' ', c.name) AS id_name FROM Cars AS c ORDER BY c.name";

$cars = $manager->executeQuery($phql);

foreach ($cars as $car) {
    echo $car->id_name, "\n";
}

Поскольку мы можем запросить полные объекты или скаляры, мы также можем запросить их одновременно:

$phql = "SELECT c.price*0.16 AS taxes, c.* FROM Cars AS c ORDER BY c.name";

$result = $manager->executeQuery($phql);

В этом случае результатом является объект Phalcon\Mvc\Model\Resultset\Complex. Это позволяет одновременно получить доступ к полным объектам и скалярам:

foreach ($result as $row) {
    echo "Name: ", $row->cars->name, "\n";
    echo "Price: ", $row->cars->price, "\n";
    echo "Taxes: ", $row->taxes, "\n";
}

Скаляры отображаются как свойства каждой «строки», а полные объекты отображаются как свойства с именем соответствующей модели.

Соединения

Легко запросить записи из нескольких моделей с помощью PHQL. Поддерживаются большинство типов соединений. Поскольку мы определили отношения в моделях, PHQL автоматически добавляет эти условия:

$phql = "SELECT Cars.name AS car_name, Brands.name AS brand_name FROM Cars JOIN Brands";

$rows = $manager->executeQuery($phql);

foreach ($rows as $row) {
    echo $row->car_name, "\n";
    echo $row->brand_name, "\n";
}

По умолчанию предполагается внутреннее соединение. Вы можете указать тип соединения в запросе:

$phql = "SELECT Cars.*, Brands.* FROM Cars INNER JOIN Brands";
$rows = $manager->executeQuery($phql);

$phql = "SELECT Cars.*, Brands.* FROM Cars LEFT JOIN Brands";
$rows = $manager->executeQuery($phql);

$phql = "SELECT Cars.*, Brands.* FROM Cars LEFT OUTER JOIN Brands";
$rows = $manager->executeQuery($phql);

$phql = "SELECT Cars.*, Brands.* FROM Cars CROSS JOIN Brands";
$rows = $manager->executeQuery($phql);

Также возможно вручную задать условия соединения:

$phql = "SELECT Cars.*, Brands.* FROM Cars INNER JOIN Brands ON Brands.id = Cars.brands_id";

$rows = $manager->executeQuery($phql);

Кроме того, соединения можно создавать, используя несколько таблиц в операторе FROM:

$phql = "SELECT Cars.*, Brands.* FROM Cars, Brands WHERE Brands.id = Cars.brands_id";

$rows = $manager->executeQuery($phql);

foreach ($rows as $row) {
    echo "Car: ", $row->cars->name, "\n";
    echo "Brand: ", $row->brands->name, "\n";
}

Если используется псевдоним для переименования моделей в запросе, эти псевдонимы будут использованы для именования атрибутов в каждой строке результата:

$phql = "SELECT c.*, b.* FROM Cars c, Brands b WHERE b.id = c.brands_id";

$rows = $manager->executeQuery($phql);

foreach ($rows as $row) {
    echo "Car: ", $row->c->name, "\n";
    echo "Brand: ", $row->b->name, "\n";
}

Когда связанная модель имеет отношение «многие ко многим» к модели «откуда», промежуточная модель неявно добавляется в сгенерированный запрос:

$phql = "SELECT Artists.name, Songs.name FROM Artists " .
        "JOIN Songs WHERE Artists.genre = 'Trip-Hop'";

$result = $this->modelsManager->executeQuery($phql);

Этот код выполняет следующий SQL в MySQL:

SELECT `artists`.`name`, `songs`.`name` FROM `artists`
INNER JOIN `albums` ON `albums`.`artists_id` = `artists`.`id`
INNER JOIN `songs` ON `albums`.`songs_id` = `songs`.`id`
WHERE `artists`.`genre` = 'Trip-Hop'

Агрегации

Следующие примеры показывают, как использовать агрегации в PHQL:

// How much are the prices of all the cars?
$phql = "SELECT SUM(price) AS summatory FROM Cars";
$row  = $manager->executeQuery($phql)->getFirst();
echo $row['summatory'];

// How many cars are by each brand?
$phql = "SELECT Cars.brand_id, COUNT(*) FROM Cars GROUP BY Cars.brand_id";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->brand_id, ' ', $row["1"], "\n";
}

// How many cars are by each brand?
$phql = "SELECT Brands.name, COUNT(*) FROM Cars JOIN Brands GROUP BY 1";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->name, ' ', $row["1"], "\n";
}

$phql = "SELECT MAX(price) AS maximum, MIN(price) AS minimum FROM Cars";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row["maximum"], ' ', $row["minimum"], "\n";
}

// Count distinct used brands
$phql = "SELECT COUNT(DISTINCT brand_id) AS brandId FROM Cars";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->brandId, "\n";
}

Условия

Условия позволяют отфильтровать набор записей, которые мы хотим запросить. Оператор WHERE позволяет это сделать:

// Simple conditions
$phql = "SELECT * FROM Cars WHERE Cars.name = 'Lamborghini Espada'";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.price > 10000";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE TRIM(Cars.name) = 'Audi R8'";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.name LIKE 'Ferrari%'";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.name NOT LIKE 'Ferrari%'";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.price IS NULL";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.id IN (120, 121, 122)";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.id NOT IN (430, 431)";
$cars = $manager->executeQuery($phql);

$phql = "SELECT * FROM Cars WHERE Cars.id BETWEEN 1 AND 100";
$cars = $manager->executeQuery($phql);

Кроме того, в PHQL подготовленные параметры автоматически экранируют входные данные, повышая безопасность:

$phql = "SELECT * FROM Cars WHERE Cars.name = :name:";
$cars = $manager->executeQuery(
    $phql,
    [
        "name" => "Lamborghini Espada"
    ]
);

$phql = "SELECT * FROM Cars WHERE Cars.name = ?0";
$cars = $manager->executeQuery(
    $phql,
    [
        0 => "Lamborghini Espada"
    ]
);

Вставка данных

С помощью PHQL можно вставлять данные, используя знакомый оператор INSERT:

// Inserting without columns
$phql = "INSERT INTO Cars VALUES (NULL, 'Lamborghini Espada', "
      . "7, 10000.00, 1969, 'Grand Tourer')";
$manager->executeQuery($phql);

// Specifying columns to insert
$phql = "INSERT INTO Cars (name, brand_id, year, style) "
      . "VALUES ('Lamborghini Espada', 7, 1969, 'Grand Tourer')";
$manager->executeQuery($phql);

// Inserting using placeholders
$phql = "INSERT INTO Cars (name, brand_id, year, style) "
      . "VALUES (:name:, :brand_id:, :year:, :style)";
$manager->executeQuery(
    $phql,
    [
        "name"     => "Lamborghini Espada",
        "brand_id" => 7,
        "year"     => 1969,
        "style"    => "Grand Tourer",
    ]
);

Phalcon не только преобразует операторы PHQL в SQL. Все события и правила бизнес-логики, определенные в модели, выполняются так, как будто мы создавали отдельные объекты вручную. Добавим правило бизнес-логики в модель автомобилей. Цена автомобиля не может быть меньше 10 000 долларов:

use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Message;

class Cars extends Model
{
    public function beforeCreate()
    {
        if ($this->price < 10000) {
            $this->appendMessage(
                new Message("A car cannot cost less than $ 10,000")
            );

            return false;
        }
    }
}

Если мы выполним следующую операцию INSERT в модели автомобилей, операция не будет успешной, так как цена не соответствует бизнес-правилу, которое мы реализовали. Проверяя статус вставки, мы можем вывести любые сообщения об ошибках валидации, сгенерированные внутри:

$phql = "INSERT INTO Cars VALUES (NULL, 'Nissan Versa', 7, 9999.00, 2015, 'Sedan')";

$result = $manager->executeQuery($phql);

if ($result->success() === false) {
    foreach ($result->getMessages() as $message) {
        echo $message->getMessage();
    }
}

Обновление данных

Обновление строк очень похоже на вставку строк. Как вам известно, инструкция для обновления записей — UPDATE. При обновлении записи события, связанные с операцией обновления, будут выполняться для каждой строки.

// Updating a single column
$phql = "UPDATE Cars SET price = 15000.00 WHERE id = 101";
$manager->executeQuery($phql);

// Updating multiples columns
$phql = "UPDATE Cars SET price = 15000.00, type = 'Sedan' WHERE id = 101";
$manager->executeQuery($phql);

// Updating multiples rows
$phql = "UPDATE Cars SET price = 7000.00, type = 'Sedan' WHERE brands_id > 5";
$manager->executeQuery($phql);

// Using placeholders
$phql = "UPDATE Cars SET price = ?0, type = ?1 WHERE brands_id > ?2";
$manager->executeQuery(
    $phql,
    [
        0 => 7000.00,
        1 => 'Sedan',
        2 => 5,
    ]
);

Оператор UPDATE выполняет обновление в двух фазах:

  • Во-первых, если оператор UPDATE имеет оператор WHERE, он извлекает все объекты, которые соответствуют этим критериям,
  • Во-вторых, на основе запрошенных объектов он обновляет/изменяет запрошенные атрибуты, сохраняя их в реляционной базе данных

Этот способ работы позволяет событиям, виртуальным внешним ключам и проверкам участвовать в процессе обновления. Кратко, следующий код:

$phql = "UPDATE Cars SET price = 15000.00 WHERE id > 101";

$result = $manager->executeQuery($phql);

if ($result->success() === false) {
    $messages = $result->getMessages();

    foreach ($messages as $message) {
        echo $message->getMessage();
    }
}

в некотором смысле эквивалентен:

$messages = null;

$process = function () use (&$messages) {
    $cars = Cars::find("id > 101");

    foreach ($cars as $car) {
        $car->price = 15000;

        if ($car->save() === false) {
            $messages = $car->getMessages();

            return false;
        }
    }

    return true;
};

$success = $process();

Удаление данных

При удалении записи события, связанные с операцией удаления, будут выполняться для каждой строки:

// Deleting a single row
$phql = "DELETE FROM Cars WHERE id = 101";
$manager->executeQuery($phql);

// Deleting multiple rows
$phql = "DELETE FROM Cars WHERE id > 100";
$manager->executeQuery($phql);

// Using placeholders
$phql = "DELETE FROM Cars WHERE id BETWEEN :initial: AND :final:";
$manager->executeQuery(
    $phql,
    [
        "initial" => 1,
        "final"   => 100,
    ]
);

Операции DELETE также выполняются в двух фазах, как и UPDATE. Чтобы проверить, вызвало ли удаление сообщения об ошибках валидации, вы должны проверить возвращенный код состояния:

// Deleting multiple rows
$phql = "DELETE FROM Cars WHERE id > 100";

$result = $manager->executeQuery($phql);

if ($result->success() === false) {
    $messages = $result->getMessages();

    foreach ($messages as $message) {
        echo $message->getMessage();
    }
}

Создание запросов с помощью конструктора запросов

Доступен конструктор для создания запросов PHQL без необходимости писать операторы PHQL, обеспечивающий также средства для IDE:

// Getting a whole set
$robots = $this->modelsManager->createBuilder()
    ->from("Robots")
    ->join("RobotsParts")
    ->orderBy("Robots.name")
    ->getQuery()
    ->execute();

// Getting the first row
$robots = $this->modelsManager->createBuilder()
    ->from("Robots")
    ->join("RobotsParts")
    ->orderBy("Robots.name")
    ->getQuery()
    ->getSingleResult();

Это то же самое, что:

$phql = "SELECT Robots.* FROM Robots JOIN RobotsParts p ORDER BY Robots.name LIMIT 20";

$result = $manager->executeQuery($phql);

Дополнительные примеры использования конструктора:

// 'SELECT Robots.* FROM Robots';
$builder->from("Robots");

// 'SELECT Robots.*, RobotsParts.* FROM Robots, RobotsParts';
$builder->from(
    [
        "Robots",
        "RobotsParts",
    ]
);

// 'SELECT * FROM Robots';
$phql = $builder->columns("*")
                ->from("Robots");

// 'SELECT id FROM Robots';
$builder->columns("id")
        ->from("Robots");

// 'SELECT id, name FROM Robots';
$builder->columns(["id", "name"])
        ->from("Robots");

// 'SELECT Robots.* FROM Robots WHERE Robots.name = "Voltron"';
$builder->from("Robots")
        ->where("Robots.name = 'Voltron'");

// 'SELECT Robots.* FROM Robots WHERE Robots.id = 100';
$builder->from("Robots")
        ->where(100);

// 'SELECT Robots.* FROM Robots WHERE Robots.type = "virtual" AND Robots.id > 50';
$builder->from("Robots")
        ->where("type = 'virtual'")
        ->andWhere("id > 50");

// 'SELECT Robots.* FROM Robots WHERE Robots.type = "virtual" OR Robots.id > 50';
$builder->from("Robots")
        ->where("type = 'virtual'")
        ->orWhere("id > 50");

// 'SELECT Robots.* FROM Robots GROUP BY Robots.name';
$builder->from("Robots")
        ->groupBy("Robots.name");

// 'SELECT Robots.* FROM Robots GROUP BY Robots.name, Robots.id';
$builder->from("Robots")
        ->groupBy(["Robots.name", "Robots.id"]);

// 'SELECT Robots.name, SUM(Robots.price) FROM Robots GROUP BY Robots.name';
$builder->columns(["Robots.name", "SUM(Robots.price)"])
    ->from("Robots")
    ->groupBy("Robots.name");

// 'SELECT Robots.name, SUM(Robots.price) FROM Robots GROUP BY Robots.name HAVING SUM(Robots.price) > 1000';
$builder->columns(["Robots.name", "SUM(Robots.price)"])
    ->from("Robots")
    ->groupBy("Robots.name")
    ->having("SUM(Robots.price) > 1000");

// 'SELECT Robots.* FROM Robots JOIN RobotsParts';
$builder->from("Robots")
    ->join("RobotsParts");

// 'SELECT Robots.* FROM Robots JOIN RobotsParts AS p';
$builder->from("Robots")
    ->join("RobotsParts", null, "p");

// 'SELECT Robots.* FROM Robots JOIN RobotsParts ON Robots.id = RobotsParts.robots_id AS p';
$builder->from("Robots")
    ->join("RobotsParts", "Robots.id = RobotsParts.robots_id", "p");

// 'SELECT Robots.* FROM Robots
// JOIN RobotsParts ON Robots.id = RobotsParts.robots_id AS p
// JOIN Parts ON Parts.id = RobotsParts.parts_id AS t';
$builder->from("Robots")
    ->join("RobotsParts", "Robots.id = RobotsParts.robots_id", "p")
    ->join("Parts", "Parts.id = RobotsParts.parts_id", "t");

// 'SELECT r.* FROM Robots AS r';
$builder->addFrom("Robots", "r");

// 'SELECT Robots.*, p.* FROM Robots, Parts AS p';
$builder->from("Robots")
    ->addFrom("Parts", "p");

// 'SELECT r.*, p.* FROM Robots AS r, Parts AS p';
$builder->from(["r" => "Robots"])
        ->addFrom("Parts", "p");

// 'SELECT r.*, p.* FROM Robots AS r, Parts AS p';
$builder->from(["r" => "Robots", "p" => "Parts"]);

// 'SELECT Robots.* FROM Robots LIMIT 10';
$builder->from("Robots")
    ->limit(10);

// 'SELECT Robots.* FROM Robots LIMIT 10 OFFSET 5';
$builder->from("Robots")
        ->limit(10, 5);

// 'SELECT Robots.* FROM Robots WHERE id BETWEEN 1 AND 100';
$builder->from("Robots")
        ->betweenWhere("id", 1, 100);

// 'SELECT Robots.* FROM Robots WHERE id IN (1, 2, 3)';
$builder->from("Robots")
        ->inWhere("id", [1, 2, 3]);

// 'SELECT Robots.* FROM Robots WHERE id NOT IN (1, 2, 3)';
$builder->from("Robots")
        ->notInWhere("id", [1, 2, 3]);

// 'SELECT Robots.* FROM Robots WHERE name LIKE '%Art%';
$builder->from("Robots")
        ->where("name LIKE :name:", ["name" => "%" . $name . "%"]);

// 'SELECT r.* FROM Store\Robots WHERE r.name LIKE '%Art%';
$builder->from(['r' => 'Store\Robots'])
        ->where("r.name LIKE :name:", ["name" => "%" . $name . "%"]);

Связанные параметры

Связанные параметры в конструкторе запросов можно задать по мере построения запроса или передать их все сразу при выполнении:

// Passing parameters in the query construction
$robots = $this->modelsManager->createBuilder()
    ->from("Robots")
    ->where("name = :name:", ["name" => $name])
    ->andWhere("type = :type:", ["type" => $type])
    ->getQuery()
    ->execute();

// Passing parameters in query execution
$robots = $this->modelsManager->createBuilder()
    ->from("Robots")
    ->where("name = :name:")
    ->andWhere("type = :type:")
    ->getQuery()
    ->execute(["name" => $name, "type" => $type]);

Запрет использования литералов в PHQL

В PHQL можно запретить использование литералов, это означает, что прямое использование строк, чисел и логических значений в строках PHQL будет запрещено. Если операторы PHQL создаются путем встраивания внешних данных, это может сделать приложение уязвимым к SQL-инъекциям:

$login = 'voltron';

$phql = "SELECT * FROM Models\Users WHERE login = '$login'";

$result = $manager->executeQuery($phql);

Если $login будет изменено на ' OR '' = ', сгенерированный оператор PHQL будет:

SELECT * FROM Models\Users WHERE login = '' OR '' = ''

Что всегда истинно независимо от того, какой логин хранится в базе данных.

Если запрещены литералы, строки могут использоваться как часть оператора PHQL, в результате будет выброшено исключение, заставляя разработчика использовать связанные параметры. Тот же запрос можно написать безопасным способом следующим образом:

$phql = "SELECT Robots.* FROM Robots WHERE Robots.name = :name:";

$result = $manager->executeQuery(
    $phql,
    [
        "name" => $name,
    ]
);

Вы можете запретить литералы следующим образом:

use Phalcon\Mvc\Model;

Model::setup(
    [
        "phqlLiterals" => false
    ]
);

Связанные параметры можно использовать, даже если литералы разрешены или нет. Запрет на использование — это просто еще одно решение по повышению безопасности веб-приложений.

Экранирование зарезервированных слов

В PHQL есть несколько зарезервированных слов. Если вы хотите использовать любое из них в качестве имен атрибутов или моделей, вам нужно экранировать эти слова с помощью разделителей экранирования для разных баз данных «[» и «]»:

$phql   = "SELECT * FROM [Update]";
$result = $manager->executeQuery($phql);

$phql   = "SELECT id, [Like] FROM Posts";
$result = $manager->executeQuery($phql);

Разделители динамически переводятся в допустимые разделители в зависимости от системы баз данных, на которой в данный момент работает приложение.

Жизненный цикл PHQL

Как высокоуровневый язык, PHQL предоставляет разработчикам возможность персонализации и настройки различных аспектов в соответствии с их потребностями. Вот жизненный цикл каждого выполняемого оператора PHQL:

  • PHQL парсится и преобразуется в промежуточное представление (IR), независимое от реализованного SQL системой базы данных.
  • IR преобразуется в допустимый SQL в соответствии с системой базы данных, связанной с моделью.
  • Операторы PHQL парсятся один раз и кэшируются в памяти. Дальнейшее выполнение того же оператора приводит к несколько более быстрому выполнению.

Использование обычного SQL

Система баз данных может предлагать определенные расширения SQL, которые не поддерживаются PHQL. В этом случае может быть уместно использование обычного SQL:

use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Resultset\Simple as Resultset;

class Robots extends Model
{
    public static function findByCreateInterval()
    {
        // A raw SQL statement
        $sql = "SELECT * FROM robots WHERE id > 0";

        // Base model
        $robot = new Robots();

        // Execute the query
        return new Resultset(
            null,
            $robot,
            $robot->getReadConnection()->query($sql)
        );
    }
}

Если в вашем приложении часто используются запросы с обычным SQL, в модель можно добавить универсальный метод:

use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Resultset\Simple as Resultset;

class Robots extends Model
{
    public static function findByRawSql($conditions, $params = null)
    {
        // A raw SQL statement
        $sql = "SELECT * FROM robots WHERE $conditions";

        // Base model
        $robot = new Robots();

        // Execute the query
        return new Resultset(
            null,
            $robot,
            $robot->getReadConnection()->query($sql, $params)
        );
    }
}

Вышеупомянутый метод findByRawSql можно использовать следующим образом:

$robots = Robots::findByRawSql(
    "id > ?",
    [
        10
    ]
);

Отладка

Некоторые моменты, которые следует учитывать при использовании PHQL:

  • Классы чувствительны к регистру; если класс не определен с тем же именем, что и при создании, это может привести к непредсказуемому поведению в системах с регистрозависимыми файловыми системами, таких как Linux.
  • Необходимо правильно определить кодировку символов при подключении, чтобы успешно связать параметры.
  • Псевдонимизированные классы не заменяются полными именами с пространством имен, так как это происходит только в PHP-коде, а не внутри строк.
  • Если включено переименование столбцов, избегайте использования псевдонимов столбцов с тем же именем, что и столбцы, которые должны быть переименованы. Это может создать путаницу в разрешении запроса.

© 2011–2017 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/latest/reference/phql.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API