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