Язык запросов Phalcon (PHQL)
Язык запросов Phalcon, PhalconQL или просто PHQL — это высокоуровневый, объектно-ориентированный диалект SQL, который позволяет писать запросы с использованием стандартизированного языка, похожего на SQL. PHQL реализован как парсер (написанный на C), который переводит синтаксис в синтаксис целевой СУБД.
Для достижения максимальной производительности Phalcon предоставляет парсер, использующий ту же технологию, что и SQLite. Эта технология обеспечивает небольшой парсер в памяти с очень низким потреблением памяти, который также потокобезопасен.
Парсер сначала проверяет синтаксис переданного заявления PHQL, затем строит промежуточное представление заявления и, наконец, преобразует его в соответствующий диалект SQL целевой СУБД.
В PHQL мы реализовали набор функций, чтобы сделать доступ к базам данных более безопасным:
- Связанные параметры являются частью языка PHQL, помогая вам защитить ваш код
- PHQL позволяет выполнять только одно SQL-заявление за вызов, предотвращая инъекции
- PHQL игнорирует все SQL-комментарии, которые часто используются в SQL-инъекциях
- PHQL допускает только операторы обработки данных, избегая случайного или внешнего изменения или удаления таблиц/баз данных без авторизации
- PHQL реализует абстракцию высокого уровня, позволяющую обрабатывать таблицы как модели, а поля — как атрибуты класса
Пример использования
Для лучшего объяснения работы PHQL рассмотрим следующий пример. У нас есть две модели «Автомобили» и «Марки»:
class Cars extends Phalcon\Mvc\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');
}
}
И каждый автомобиль принадлежит к определенной марке, поэтому у марки может быть много автомобилей:
class Brands extends Phalcon\Mvc\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:
// Instantiate the Query
$query = new Phalcon\Mvc\Model\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(array(
'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:", array(
'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);
Большая часть стандарта SQL поддерживается PHQL, даже нестандартные директивы, такие как 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(array("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 Brands.name, Songs.name FROM Artists ' .
'JOIN Songs WHERE Artists.genre = "Trip-Hop"';
$result = $this->modelsManager->query($phql);
Этот код генерирует следующий SQL в MySQL:
SELECT `brands`.`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, array("name" => 'Lamborghini Espada'));
$phql = "SELECT * FROM Cars WHERE Cars.name = ?0";
$cars = $manager->executeQuery($phql, array(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($sql,
array(
'name' => 'Lamborghini Espada',
'brand_id' => 7,
'year' => 1969,
'style' => 'Grand Tourer',
)
);
Phalcon не просто преобразует заявления PHQL в SQL. Все события и правила бизнеса, определенные в модели, выполняются так, как если бы мы создавали отдельные объекты вручную. Добавьте правило бизнеса в модели автомобилей. Стоимость автомобиля не может быть меньше 10 000 долларов:
use Phalcon\Mvc\Model\Message;
class Cars extends Phalcon\Mvc\Model
{
public function beforeCreate()
{
if ($this->price < 10000)
{
$this->appendMessage(new Message("A car cannot cost less than $ 10,000"));
return false;
}
}
}
Если мы выполним следующую вставку в модели автомобилей, операция не будет успешной, потому что цена не соответствует нашему правилу бизнеса:
$phql = "INSERT INTO Cars VALUES (NULL, 'Nissan Versa', 7, 9999.00, 2012, '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, array(
0 => 7000.00,
1 => 'Sedan',
2 => 5
));
Оператор UPDATE выполняет обновление в двух фазах:
- Во-первых, если у UPDATE есть условие WHERE, он извлекает все объекты, соответствующие этим критериям,
- Во-вторых, основываясь на запрошенных объектах, он обновляет/изменяет запрошенные атрибуты, сохраняя их в реляционной базе данных
Такой способ работы позволяет событиям, виртуальным внешним ключам и проверкам участвовать в процессе обновления. Вкратце, следующий код:
$phql = "UPDATE Cars SET price = 15000.00 WHERE id > 101"; $success = $manager->executeQuery($phql);
в некоторой степени эквивалентен:
$messages = null;
$process = function() use (&$messages) {
foreach (Cars::find("id > 101") 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,
array(
'initial' => 1,
'final' => 100
)
);
Операции DELETE также выполняются в двух фазах, как и UPDATE.
Создание запросов с помощью конструктора запросов
Доступен конструктор для создания запросов 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(array('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(array('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(array('Robots.name', 'Robots.id'));
// 'SELECT Robots.name, SUM(Robots.price) FROM Robots GROUP BY Robots.name';
$builder->columns(array('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(array('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(array('r' => 'Robots'))
->addFrom('Parts', 'p');
// 'SELECT r.*, p.* FROM Robots AS r, Parts AS p';
$builder->from(array('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', array(1, 2, 3));
// 'SELECT Robots.* FROM Robots WHERE id NOT IN (1, 2, 3)';
$builder->from('Robots')
->notInWhere('id', array(1, 2, 3));
// 'SELECT Robots.* FROM Robots WHERE name LIKE '%Art%';
$builder->from('Robots')
->where('name LIKE :name:', array('name' => '%' . $name . '%'));
// 'SELECT r.* FROM Store\Robots WHERE r.name LIKE '%Art%';
$builder->from(['r' => 'Store\Robots'])
->where('r.name LIKE :name:', array('name' => '%' . $name . '%'));
Связанные параметры
Связанные параметры в конструкторе запросов можно задавать по мере построения запроса или передавать их все сразу при выполнении:
//Passing parameters in the query construction
$robots = $this->modelsManager->createBuilder()
->from('Robots')
->where('name = :name:', array('name' => $name))
->andWhere('type = :type:', array('type' => $type))
->getQuery()
->execute();
//Passing parameters in query execution
$robots = $this->modelsManager->createBuilder()
->from('Robots')
->where('name = :name:')
->andWhere('type = :type:')
->getQuery()
->execute(array('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 '' = ''"
Что всегда истинно, независимо от того, что хранится в базе данных как значение login.
Если литералы запрещены, строки могут использоваться в качестве части заявления PHQL, при этом будет выброшено исключение, заставляя разработчика использовать связанные параметры. Тот же запрос можно записать безопасным способом так:
$phql = "SELECT Robots.* FROM Robots WHERE Robots.name = :name:";
$result = $manager->executeQuery($phql, array('name' => $name));
Отключить литералы можно следующим образом:
Phalcon\Mvc\Model::setup(array('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\Resultset\Simple as Resultset;
class Robots extends Phalcon\Mvc\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\Resultset\Simple as Resultset;
class Robots extends Phalcon\Mvc\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 > ?', array(10));
Устранение неполадок
Некоторые моменты, которые следует учитывать при использовании PHQL:
- Классы чувствительны к регистру. Если класс не определен с тем же именем, что и при его создании, это может привести к непредсказуемому поведению в операционных системах с регистрозависимой файловой системой, таких как Linux.
- Необходимо правильно определить кодировку в подключении, чтобы успешно привязать параметры.
- Псевдонимы классов не заменяются полными именами пространств имен, поскольку это происходит только в коде PHP, а не внутри строк.
- Если включено переименование столбцов, избегайте использования псевдонимов столбцов с теми же именами, что и столбцы, которые нужно переименовать. Это может привести к путанице в резольвере запросов.
© 2011–2016 Phalcon Framework Team
Licensed under the Creative Commons Attribution License 3.0.
https://docs.phalconphp.com/en/2.0.0/reference/phql.html