[TOC]
# Phalcon查询语言(PHQL)
Phalcon查询语言,PhalconQL或简称PHQL是一种高级,面向对象的SQL方言,允许使用标准化的类SQL语言编写查询。PHQL实现为解析器(用C编写),用于转换目标RDBMS的语法。
为了实现最高性能,Phalcon提供了一个使用与[SQLite](http://en.wikipedia.org/wiki/Lemon_Parser_Generator) 相同技术的解析器。该技术提供了一个内存小的解析器,内存占用非常少,同时也是线程安全的。
解析器首先检查传递PHQL语句的语法,然后构建语句的中间表示,最后将其转换为目标RDBMS的相应SQL方言。
在PHQL中,我们实现了一组功能,使您对数据库的访问更加安全:
* 绑定参数是PHQL语言的一部分,可帮助您保护代码
* PHQL仅允许每次调用执行一个SQL语句以防止注入
* PHQL忽略SQL注入中经常使用的所有SQL注释
* PHQL仅允许数据操作语句,避免错误地或在未经授权的情况下外部更改或删除表/数据库
* PHQL实现了高级抽象,允许您将表作为模型和字段处理为类属性
## 用例
为了更好地解释PHQL如何工作,请考虑以下示例。我们有两个模型`Cars`和`Brands`:
```php
<?php
use Phalcon\Mvc\Model;
class Cars extends Model
{
public $id;
public $name;
public $brand_id;
public $price;
public $year;
public $style;
/**
* 此模型映射到表sample_cars
*/
public function getSource()
{
return 'sample_cars';
}
/**
* 一辆车只有一个Brand,但一个Brand有很多Cars
*/
public function initialize()
{
$this->belongsTo('brand_id', 'Brands', 'id');
}
}
```
每辆车都有一个品牌,所以品牌有很多车:
```php
<?php
use Phalcon\Mvc\Model;
class Brands extends Model
{
public $id;
public $name;
/**
* 模型品牌被映射到'sample_brands'表
*/
public function getSource()
{
return 'sample_brands';
}
/**
* 一个品牌可以有很多汽车
*/
public function initialize()
{
$this->hasMany('id', 'Cars', 'brand_id');
}
}
```
## 创建PHQL查询
只需实例化 `Phalcon\Mvc\Model\Query`类即可创建PHQL查询:
```php
<?php
use Phalcon\Mvc\Model\Query;
// 实例化查询
$query = new Query(
'SELECT * FROM Cars',
$this->getDI()
);
// 执行返回结果的查询(如果有)
$cars = $query->execute();
```
从控制器或视图中,使用注入的`models manager`(`Phalcon\Mvc\Model\Manager`)可以轻松创建/执行它们:
```php
<?php
// 执行简单的查询
$query = $this->modelsManager->createQuery('SELECT * FROM Cars');
$cars = $query->execute();
// 带有绑定参数
$query = $this->modelsManager->createQuery('SELECT * FROM Cars WHERE name = :name:');
$cars = $query->execute(
[
'name' => 'Audi',
]
);
```
或者只是执行它:
```php
<?php
// 执行简单的查询
$cars = $this->modelsManager->executeQuery(
'SELECT * FROM Cars'
);
// 带有绑定参数
$cars = $this->modelsManager->executeQuery(
'SELECT * FROM Cars WHERE name = :name:',
[
'name' => 'Audi',
]
);
```
## 选取记录
作为熟悉的SQL,PHQL允许使用我们所知道的SELECT语句查询记录,除了不使用指定表,我们使用模型类:
```php
<?php
$query = $manager->createQuery(
'SELECT * FROM Cars ORDER BY Cars.name'
);
$query = $manager->createQuery(
'SELECT Cars.name FROM Cars ORDER BY Cars.name'
);
```
命名空间中的类也是允许的:
```php
<?php
$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:
```php
<?php
$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`。这种结果集是一组完整的模型对象:
```php
<?php
$phql = 'SELECT c.* FROM Cars AS c ORDER BY c.name';
$cars = $manager->executeQuery($phql);
foreach ($cars as $car) {
echo 'Name: ', $car->name, "\n";
}
```
这完全相同:
```php
<?php
$cars = Cars::find(
[
'order' => 'name'
]
);
foreach ($cars as $car) {
echo 'Name: ', $car->name, "\n";
}
```
可以修改完整对象并将其重新保存在数据库中,因为它们代表关联表的完整记录。还有其他类型的查询不返回完整对象,例如:
```php
<?php
$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允许您查询所有类型的标量:字段,函数,文字,表达式等:
```php
<?php
$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";
}
```
由于我们可以查询完整的对象或标量,我们也可以同时查询两个:
```php
<?php
$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`。这允许一次访问完整对象和标量:
```php
<?php
foreach ($result as $row) {
echo 'Name: ', $row->cars->name, "\n";
echo 'Price: ', $row->cars->price, "\n";
echo 'Taxes: ', $row->taxes, "\n";
}
```
标量映射为每个“行”的属性,而完整对象则映射为具有其相关模型名称的属性。
### 连接
使用PHQL从多个模型中请求记录很容易。支持大多数种类的连接。当我们在模型中定义关系时,PHQL会自动添加这些条件:
```php
<?php
$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";
}
```
默认情况下,假定为INNER JOIN。您可以在查询中指定JOIN的类型:
```php
<?php
$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);
```
也可以手动设置JOIN的条件:
```php
<?php
$phql = 'SELECT Cars.*, Brands.* FROM Cars INNER JOIN Brands ON Brands.id = Cars.brands_id';
$rows = $manager->executeQuery($phql);
```
此外,可以使用FROM子句中的多个表创建联接:
```php
<?php
$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";
}
```
如果使用别名重命名查询中的模型,那么将使用这些别名来命名结果的每一行中的属性:
```php
<?php
$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";
}
```
当联接模型与from模型具有多对多关系时,中间模型将隐式添加到生成的查询中:
```php
<?php
$phql = 'SELECT Artists.name, Songs.name FROM Artists ' .
'JOIN Songs WHERE Artists.genre = "Trip-Hop"';
$result = $this->modelsManager->executeQuery($phql);
```
此代码在MySQL中执行以下SQL:
```sql
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中使用聚合:
```php
<?php
// 所有车的价格是多少?
$phql = 'SELECT SUM(price) AS summatory FROM Cars';
$row = $manager->executeQuery($phql)->getFirst();
echo $row['summatory'];
// 每个品牌有多少辆汽车?
$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";
}
// 每个品牌有多少辆汽车?
$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";
}
// 统计不同的二手品牌
$phql = 'SELECT COUNT(DISTINCT brand_id) AS brandId FROM Cars';
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
echo $row->brandId, "\n";
}
```
### 条件
条件允许我们过滤我们想要查询的记录集。WHERE子句允许这样做:
```php
<?php
// 简单条件
$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的一部分,准备好的参数会自动转义输入数据,从而带来更多安全性:
```php
<?php
$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语句插入数据:
```php
<?php
// 插入没有列
$phql = 'INSERT INTO Cars VALUES (NULL, "Lamborghini Espada", '
. '7, 10000.00, 1969, "Grand Tourer")';
$manager->executeQuery($phql);
// 指定要插入的列
$phql = 'INSERT INTO Cars (name, brand_id, year, style) '
. 'VALUES ("Lamborghini Espada", 7, 1969, "Grand Tourer")';
$manager->executeQuery($phql);
// 使用占位符插入
$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:
```php
<?php
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;
}
}
}
```
如果我们在模型Cars中进行了以下 `INSERT` ,则操作将不会成功,因为价格不符合我们实施的业务规则。通过检查插入的状态,我们可以打印内部生成的任何验证消息:
```php
<?php
$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();
}
}
```
## 更新数据
更新行与插入行非常相似。您可能知道,更新记录的指令是更新。更新记录时,将对每行执行与更新操作相关的事件。
```php
<?php
// 更新单个列
$phql = 'UPDATE Cars SET price = 15000.00 WHERE id = 101';
$manager->executeQuery($phql);
// 更新多个列
$phql = 'UPDATE Cars SET price = 15000.00, type = "Sedan" WHERE id = 101';
$manager->executeQuery($phql);
// 更新多行
$phql = 'UPDATE Cars SET price = 7000.00, type = "Sedan" WHERE brands_id > 5';
$manager->executeQuery($phql);
// 使用占位符
$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` 子句,它将检索符合这些条件的所有对象,
* 其次,基于查询的对象,它更新/更改将它们存储到关系数据库的请求属性
这种操作方式允许事件,虚拟外键和验证参与更新过程。总之,以下代码:
```php
<?php
$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();
}
}
```
有点相当于:
```php
<?php
$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();
```
## 删除数据
删除记录时,将对每行执行与删除操作相关的事件:
```php
<?php
// 删除单行
$phql = 'DELETE FROM Cars WHERE id = 101';
$manager->executeQuery($phql);
// 删除多行
$phql = 'DELETE FROM Cars WHERE id > 100';
$manager->executeQuery($phql);
// 使用占位符
$phql = 'DELETE FROM Cars WHERE id BETWEEN :initial: AND :final:';
$manager->executeQuery(
$phql,
[
'initial' => 1,
'final' => 100,
]
);
```
`DELETE` 操作也分两个阶段执行,如 `UPDATE`。要检查删除是否产生任何验证消息,您应该检查返回的状态代码:
```php
<?php
// 删除多行
$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工具:
```php
<?php
// 获取整个集合
$robots = $this->modelsManager->createBuilder()
->from('Robots')
->join('RobotsParts')
->orderBy('Robots.name')
->getQuery()
->execute();
// 获取第一行
$robots = $this->modelsManager->createBuilder()
->from('Robots')
->join('RobotsParts')
->orderBy('Robots.name')
->getQuery()
->getSingleResult();
```
这与以下相同:
```php
<?php
$phql = 'SELECT Robots.* FROM Robots JOIN RobotsParts p ORDER BY Robots.name LIMIT 20';
$result = $manager->executeQuery($phql);
```
更多生成器的例子:
```php
<?php
// '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 . '%']);
```
### 绑定参数
查询构建器中的绑定参数可以在执行时一次构造或过去查询时设置:
```php
<?php
// 在查询构造中传递参数
$robots = $this->modelsManager->createBuilder()
->from('Robots')
->where('name = :name:', ['name' => $name])
->andWhere('type = :type:', ['type' => $type])
->getQuery()
->execute();
// 在查询执行中传递参数
$robots = $this->modelsManager->createBuilder()
->from('Robots')
->where('name = :name:')
->andWhere('type = :type:')
->getQuery()
->execute(['name' => $name, 'type' => $type]);
```
## 在PHQL中禁止文字
可以在PHQL中禁用文字,这意味着不允许直接在PHQL字符串中使用字符串,数字和布尔值。如果创建了PHQL语句,并在其上嵌入外部数据,则可以打开应用程序以进行潜在的SQL注入:
```php
<?php
$login = 'voltron';
$phql = "SELECT * FROM Models\Users WHERE login = '$login'";
$result = $manager->executeQuery($phql);
```
如果 `$login` 更改为 `' OR '' = '`,则生成的PHQL为:
```sql
SELECT * FROM Models\Users WHERE login = '' OR '' = ''
```
无论数据库中存储的是什么,都始终`true`。
如果不允许文字,则可以将字符串用作PHQL语句的一部分,因此将抛出异常,迫使开发人员使用绑定参数。可以用以下安全方式编写相同的查询:
```php
<?php
$type = 'virtual';
$phql = 'SELECT Robots.* FROM Robots WHERE Robots.type = :type:';
$result = $manager->executeQuery(
$phql,
[
'type' => $type,
]
);
```
您可以通过以下方式禁用文字:
```php
<?php
use Phalcon\Mvc\Model;
Model::setup(
[
'phqlLiterals' => false
]
);
```
即使允许或不允许文字,也可以使用绑定参数。禁止它们只是开发人员可以在Web应用程序中采取的另一个安全决策。
## 转义保留字
PHQL有一些保留字,如果要将它们中的任何一个用作属性或模型名称,则需要使用跨数据库转义分隔符 `[` 和 `]`来转义这些字:
```php
<?php
$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
数据库系统可以提供PHQL不支持的特定SQL扩展,在这种情况下,原始SQL可能是合适的:
```php
<?php
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Resultset\Simple as Resultset;
class Robots extends Model
{
public static function findByCreateInterval()
{
// 原始SQL语句
$sql = 'SELECT * FROM robots WHERE id > 0';
// 基础模型
$robot = new Robots();
// 执行查询
return new Resultset(
null,
$robot,
$robot->getReadConnection()->query($sql)
);
}
}
```
如果原始SQL查询在您的应用程序中很常见,则可以在模型中添加通用方法:
```php
<?php
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Resultset\Simple as Resultset;
class Robots extends Model
{
public static function findByRawSql($conditions, $params = null)
{
// 原始SQL语句
$sql = 'SELECT * FROM robots WHERE $conditions';
// 基础模型
$robot = new Robots();
// 执行查询
return new Resultset(
null,
$robot,
$robot->getReadConnection()->query($sql, $params)
);
}
}
```
上面的`findByRawSql`可以用如下:
```php
<?php
$robots = Robots::findByRawSql(
'id > ?',
[
10
]
);
```
## 故障排除
使用PHQL时要记住的一些事项:
* 类是区分大小写的,如果没有使用与创建时相同的名称定义类,这可能会导致在具有区分大小写的文件系统(如Linux)的操作系统中出现意外行为。
* 必须在连接中定义正确的字符集才能成功绑定参数。
* 别名类不会被完整的命名空间类替换,因为这只发生在PHP代码中,而不是在字符串内。
* 如果启用了列重命名,请避免使用与要重命名的列具有相同名称的列别名,这可能会使查询解析程序混淆。
- 常规
- Welcome
- 贡献
- 生成回溯
- 测试重现
- 单元测试
- 入门
- 安装
- Web服务器设置
- WAMP
- XAMPP
- 教程
- 基础教程
- 教程:创建一个简单的REST API
- 教程:Vökuró
- 提升性能
- 教程:INVO
- 开发环境
- Phalcon Compose (Docker)
- Nanobox
- Phalcon Box (Vagrant)
- 开发工具
- Phalcon开发者工具的安装
- Phalcon开发者工具的使用
- 调试应用程序
- 核心
- MVC应用
- 微应用
- 创建命令行(CLI)应用程序
- 依赖注入与服务定位
- MVC架构
- 服务
- 使用缓存提高性能
- 读取配置
- 上下文转义
- 类加载器
- 使用命名空间
- 日志
- 队列
- 数据库
- 数据库抽象层
- Phalcon查询语言(PHQL)
- ODM(对象文档映射器)
- 使用模型
- 模型行为
- ORM缓存
- 模型事件
- 模型元数据
- 模型关系
- 模型事务
- 验证模型
- 数据库迁移
- 分页
- 前端
- Assets管理
- 闪存消息
- 表单
- 图像
- 视图助手(标签)
- 使用视图
- Volt:模板引擎
- 业务逻辑
- 访问控制列表(ACL)
- 注解解析器
- 控制器
- 调度控制器
- 事件管理器
- 过滤与清理
- 路由
- 在session中存储数据
- 生成URL和路径
- 验证
- HTTP
- Cookies管理
- 请求环境
- 返回响应
- 安全
- 加密/解密
- 安全
- 国际化
- 国际化
- 多语言支持