资讯专栏INFORMATION COLUMN

Phalcon查询语言

Moxmi / 1453人阅读

摘要:查询语言查询语言,简称或,是一种面向对象的高级语言,允许用标准化的编写。该对象的每个成员都是一个包含所查询字段的标准对象。

Phalcon查询语言(Phalcon Query Language)

Phalcon查询语言,简称PhalconQL或PHQL,是一种面向对象的高级SQL语言,允许用标准化的SQL编写。PHQL实现了把操作语句解析为RDBMS目标语言的解析器(C语言编写)。

为了达到最佳性能,Phalcon提供了与SQLite相同的解析器,其线程安全,内存占用极低。

解析器先检查传递的PHQL语句的语法,然后构建中间语句,最后将其转换为RDBMS对应的SQL语句。

PHQL实现了一系列功能,可以更安全的操作数据库。

参数绑定是PHQL功能之一,使代码更安全

PHQL每次只允许执行一条SQL语句,以防SQL注入

PHQL会忽略所有SQL注入中常用的SQL注释

PHQL只允许数据操作语句,避免错误的或未经授权的更改、删除数据库和表

PHQL实现了高级抽象接口,允许以模型方式操作表,以类属性方式操作表字段

使用示例(Usage Example)

为了更好的解释PHQL工作原理,请参考下例。有CarsBrands两个模型:

belongsTo("brand_id", "Brands", "id");
    }
}

每辆车都属于一个品牌,每个品牌有多辆车:

hasMany("id", "Cars", "brand_id");
    }
}
创建PHQL查询(Creating PHQL Queries)

实例化PhalconMvcModelQuery类即可创建PHQL查询:

getDI()
);

// 执行查询,返回结果(如果有的话)
$cars = $query->execute();

控制器或视图中,使用PhalconMvcModelManager可以很容易的创建、执行PHQL查询:

modelsManager->createQuery("SELECT * FROM Cars");
$cars  = $query->execute();

// 使用参数绑定
$query = $this->modelsManager->createQuery("SELECT * FROM Cars WHERE name = :name:");
$cars  = $query->execute(
    [
        "name" => "Audi",
    ]
);

或者直接执行查询:

modelsManager->executeQuery(
    "SELECT * FROM Cars"
);

// 使用参数绑定
$cars = $this->modelsManager->executeQuery(
    "SELECT * FROM Cars WHERE name = :name:",
    [
        "name" => "Audi",
    ]
);
查询记录(Selecting Records)

PHQL允许使用我们熟知的SELECT语句查询记录,使用模型名字代替表名:

createQuery(
    "SELECT * FROM Cars ORDER BY Cars.name"
);

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

允许带命名空间的模型名:

createQuery($phql);

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

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

PHQL支持大部分标准SQL语法,非标准的SQL语法也同样支持,如LIMIT:

createQuery($phql);
结果集类型(Result Types)

结果集类型根据我们查询字段的不同而不同,如果检索单个完整对象,则返回PhalconMvcModelResultsetSimple对象。这种结果集是一组完整的模型对象:

executeQuery($phql);

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

下面这种方式也一样:

 "name",
    ]
);

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

完整模型对象中的数据能够被修改,并重新保存到数据库中,因为它们代表关联表的完整记录。下面这种查询方式不会返回完整模型对象:

executeQuery($phql);

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

我们仅仅查询了表中的某些字段,虽然返回的结果集仍然是PhalconMvcModelResultsetSimple对象,但不能当成完整模型对象。该对象的每个成员都是一个包含所查询字段的标准对象。

这些不表示完整对象的值就是我们所说的标量,PHQL允许查询所有类型的标量:字段,函数,字面两,表达式等:

execute($phql);

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

我们可以查询完整对象或标量,也可以同时查询它们:

executeQuery($phql);

这种情况下的结果集是一个PhalconMvcModelResultsetComplex对象,可以同时访问完整对象和标量:

cars->name, "
";
    echo "Price: ", $row->cars->price, "
";
    echo "Taxes: ", $row->taxes, "
";
}
连接(Joins)

使用PHQL可以很容易的从多个模型请求记录,支持大部分的JOIN方式。我们在模型中定义关系之后,PHQL会自动添加这些条件:

executeQuery($phql);

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

默认使用INNER JOIN,可以指定JOIN类型:

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条件:

executeQuery($phql);

如果查询中为模型定义别名,则将使用别名为结果集中的每一条记录命名:

executeQuery($phql);

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

如果连接模型与from之后的模型具有多对多关系时,中间模型将隐式的添加到查询中:

modelsManager->executeQuery($phql);

上述代码在MySQL中执行下列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"
聚合(Aggregations)

下面例子展示了PHQL中如何使用聚合:

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"], "
";
}

// 每个品牌下的汽车总数
$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"], "
";
}

$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"], "
";
}

// 统计品牌数量
$phql = "SELECT COUNT(DISTINCT brand_id) AS brandId FROM Cars";
$rows = $manager->executeQuery($phql);
foreach ($rows as $row) {
    echo $row->brandId, "
";
}
条件(Conditions)

条件能让我们过滤想要查询的记录,WHERE子句允许这样:

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的一部分,参数绑定会自动转义输入数据,安全性更高:

executeQuery(
    $phql,
    [
        "name" => "Lamborghini Espada",
    ]
);

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

通过PHQL,可以使用我们非常熟悉的INSERT语句插入数据:

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,模型中定义的所有事件和业务规则都会执行,就像我们手动创建对象那样。我们为模型Cars创建一条规则,车的价格不能低于$ 10,000:

price < 10000) {
            $this->appendMessage(
                new Message("A car cannot cost less than $ 10,000")
            );

            return false;
        }
    }
}

如果我们在模型Cars中执行下面的INSERT语句,操作将会失败,因为price不满足我们制定的规则。通过检查插入状态,我们可以打印任何内部生成的验证消息:

executeQuery($phql);

if ($result->success() === false) {
    foreach ($result->getMessages() as $message) {
        echo $message->getMessage();
    }
}
更新数据(Updating Data)

更新记录与插入记录非常相似,更新记录使用UPDATE命令。更新记录时,将为每条记录执行与更新操作相关的事件。

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子句,将检索符合条件的所有对象

其次,基于查询对象更新字段并保存

这种操作方式允许事件、虚拟外键和验证参与更新过程。

 101";

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

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

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

上面代码相当于:

 101");

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

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

            return false;
        }
    }

    return true;
};

$success = $process();
删除数据(Deleting Data)

删除记录时,与删除操作相关的事件将逐一执行:

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,
    ]
);

UPDATE一样,DELETE操作也分两步执行,要检查删除操作是否产生验证消息,你可以检查返回的状态:

 100";

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

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

    foreach ($messages as $message) {
        echo $message->getMessage();
    }
}
使用查询构造器创建查询(Creating queries using the Query Builder)

查询构造器可用于创建PHQL查询,无需编写PHQL语句:

modelsManager->createBuilder()
    ->from("Robots")
    ->join("RobotsParts")
    ->orderBy("Robots.name")
    ->getQuery()
    ->execute();

// 获取第一条记录
$robots = $this->modelsManager->createBuilder()
    ->from("Robots")
    ->join("RobotsParts")
    ->orderBy("Robots.name")
    ->getQuery()
    ->getSingleResult();

同下列操作:

executeQuery($phql);

查询构造器更多示例:

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("RobotsParts", "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, 10);

// "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 StoreRobots WHERE r.name LIKE "%Art%"";
$builder->from(["r" => "StoreRobots"])
    ->where("r.name LIKE :name:", ["name" => "%" . $name . "%"]);
参数绑定(Bound Parameters)

查询构造器中的参数绑定可以在查询构建时设置,也可以在查询执行时设置:

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]);
禁用字面量(Disallow literals in PHQL)

PHQL中可以禁用字面量,这意味着如果禁用开启,则不能在PHQL语句中直接使用PHP字符串、数字和布尔值。如果在PHQL语句中嵌入外部数据,可能导致潜在的注入攻击:

executeQuery($phql);

如果$login的值为" OR " " = " ,将产生如下PHQL语句:

SELECT * FROM ModelsUsers WHERE login = "" OR "" = "";

无论存储在数据库中的login是何值,条件总是true

如果字面量被禁用,在PHQL中使用PHP字面量会抛出异常,以强制开发者使用参数绑定。上面的查询这样写更安全:

executeQuery(
    $phql,
    [
        "type" => $type,
    ]
);

可以通过以下方式禁用字面量:

 false]
);

无论字面量是否禁用,参数绑定都可以正常使用。禁用只是开发人员能够在web应用中采取的一项安全策略。

转义保留字(Escaping Reserved Words)

PHQL有一些保留字,如果想将保留字作为模型名或字段名使用,则需要使用转义分隔符[]来转义关键字:

executeQuery($phql);

$phql   = "SELECT id, [Like] FROM Posts";
$result = $manager->executeQuery($phql);
PHQL生命周期(PHQL Lifecycle)

作为高级语言,PHQL赋予了开发者个性化定制的能力,以满足不同的需求。以下是PHQL语句的生命周期:

PHQL被解析并转换为独立于数据库SQL之外的中间表示(IR)

根据模型对应的数据库系统,IR被转换为有效的SQL

PHQL语句被解析并保存在内存中,再次执行相同语句时速度会更快

使用原生SQL(Using Raw SQL)

某些数据库系统可能会提供PHQL不支持的特殊SQL扩展,这种情况适合使用原生SQL:

 0";

        // 模型
        $robot = new Robots();

        // 执行查询
        return new Resultset(
            null,
            $robot,
            $robot->getReadConnection()->query($sql)
        );
    }
}

如果原生SQL查询在应用中很普遍,可以在模型中添加通用方法:

getReadConnection()->query($sql),
        );
    }
}

上述findByRawSQL可以如下使用:

 ?",
    [
        10,
    ]
);
注意事项(Troubleshooting)

PHQL中的一些注意事项:

类名称区分大小写,如果定义类时名称和创建时的名称不一致,在大小写敏感的操作系统(如linux)中将导致不可预知行为

为保证参数绑定成功,连接数据库时必须指定正确的字符集

指定别名的类不能用完整命名空间替换,因为这项操作发生在PHP代码中,而非PHQL语句里

如果字段使用别名,应避免别名和字段名相同,不然查询解析器容易混淆。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/28527.html

相关文章

  • PHP-Phalcon框架中的数据库操作

    摘要:本文描述了框架中数据库操作方法,主要讨论框架的组件中的操作方法。属性方法在框架中支持属性的扩展查询,在上例中,可以把条件语句改为同时省略查询条件结果不变。 本文描述了PHP-Phalcon框架中数据库操作方法,主要讨论Phalcon框架的Model组件中的操作方法。更详细的Model介绍请参考:官方文档 1. 连接数据库 在Phalcon框架中,通过在DI中注入db参数来实现数据库的...

    xiaotianyi 评论0 收藏0
  • phalcon使用经验杂谈

    摘要:初次认识是在刚学的时候最近终于有机会用上了故此说说使用上的一些感受个人是很喜欢这套框架的方式使用依赖注入让代码组织很灵活耦合也很低但是也许是框架东西太多了遇到几个坑上一年以上的也还没解决不过有一定开发经验的话还是可以自己修复的被这几个坑浪费 初次认识phalcon是在刚学php的时候,最近终于有机会用上了.故此说说使用上的一些感受 个人是很喜欢phalcon这套框架的方式,使用...

    UsherChen 评论0 收藏0
  • phalcon简易指南

    摘要:帮助你开始使用的简易指南。第一种方式参考第二种方式参考使用参考简单粗暴的理解是把下的对应成数据库的表,类属性对应表字段。 帮助你开始使用 phalcon 的简易指南。 简介 Phalcon 2将于2015年4月17日发布,这个版本大约85%的代码是基于 Zephir 语言重写的。Zephir是开源的,使用类似PHP语法的语言,生成C语言代码,并编译成PHP扩展。这提高了PHP扩展的开发...

    whataa 评论0 收藏0
  • PHP7 安装Phalcon框架

    摘要:是什么是开源全功能栈使用扩展编写针对高性能优化的框架。也是松耦合的,可以根据项目的需要任意使用其他对象。安装支持版本的不支持普通方式的编译安装,只能通过安装。因此安装之前,请先安装。 Phalcon 是什么? Phalcon 是开源、全功能栈、使用 C 扩展编写、针对高性能优化的 PHP 5 框架。 开发者不需要学习和使用 C 语言的功能, 因为所有的功能都以 PHP 类的方式暴露出来...

    kidsamong 评论0 收藏0
  • phalcon 中常用的phql语句

    摘要:先引入类确定查询表关联表需要查询的字段,这里两个表的字段都可以当数据很大时,统计数据时用条件你好执行搜索执行模糊搜索设置条件,什么的都可以往后加注意这里的条件和原始语句中的语句刚好相反获取查询对象执行并返回结果 先引入Builder类 use PhalconMvcModelQueryBuilder as QueryBuilder; public function mytestActio...

    jsummer 评论0 收藏0

发表评论

0条评论

Moxmi

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<