使用Phalcon的Models
模型表示应用程序的信息(数据)和操作该数据的规则。模型主要用于管理与相应数据库表的交互规则。在大多数情况下,数据库中的每个表都对应于应用程序中的一个模型。应用程序的大部分业务逻辑将集中在模型中。
Phalcon\Mvc\Model是Phalcon应用程序中所有模型的基础。它提供了数据库独立性、基本的CRUD功能、高级查找功能以及将模型相互关联的能力,以及其他服务。Phalcon\Mvc\Model避免了必须使用SQL语句的需要,因为它动态地将方法转换为相应的数据库引擎操作。
模型的目的是在较高的抽象层上使用数据库。如果您需要使用较低级别的数据库,请查看Phalcon\Db组件文档。
1. 创建模型
模型是一个从Phalcon\Mvc\ model扩展而来的类。它的类名应该是驼峰大小写:
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class RobotParts extends Model
{
}
默认情况下,模型’ Store\Toys\RobotParts ‘将映射到表’ robot_parts ‘。如果你想手动将模型映射到另一个表名,可以使用’ setSource() '方法:
function initialize(){
$this->setSource('toys_robot_parts');
}
模型’ RobotParts ‘现在映射到’ toys_robot_parts ‘表。’ initialize() '方法帮助使用自定义行为(即不同的表)建立这个模型。
’ initialize() ‘方法在请求期间只被调用一次。此方法会在创建模型的每个实例时执行,来实现统一的初始化。如果你想为创建的每个实例执行初始化任务,可以使用’ onConstruct() '方法:
1.1 Public属性和 Setters/Getters
模型可以使用Public属性来实现,每个Public属性都可以被模型实例华后的任何一部分代码所读取和更改。
另一种实现是使用getter和setter函数,它们控制哪些属性对该模型公开可用。使用getter和setter的好处是,开发人员可以对模型设置的值执行转换和验证检查,这在使用公共属性时是不可能的。此外,getter和setter允许在不改变模型类接口的情况下进行未来的更改。因此,如果字段名称发生了变化,只需要在相关getter/setter中引用的模型的私有属性中进行更改,而不需要在代码中的其他地方进行更改。
公共属性在开发中提供较少的复杂性。然而,getter /setter可以极大地提高应用程序的可测试性、可扩展性和可维护性。开发人员可以根据应用程序的需求决定哪种策略更适合他们正在创建的应用程序。ORM与定义属性的两种方案都兼容。
属性名中的下划线在使用getter和setter时可能会有问题。
必须在getter/setter的命名中使用驼峰大小写来使用带下划线的内置属性名,如:’ $model->getPropertyName ‘而不是’ $model->getProperty_name ', ’ $model->findByPropertyName ‘而不是’ $model->findByProperty_name ',等等。
由于大部分系统都希望使用驼峰大小写,并且通常会删除下划线,因此属性名的命名也建议按照上述方式,您可以使用列映射来确保将属性映射到对应的数据库。
2. 理解记录和对象
模型的每个实例(对象)都表示表中的一行记录。
可以通过读取对象属性轻松访问记录数据。例如,对于包含记录的表’ robots ':
mysql> select * from robots;
+----+------------+------------+------+
| id | name | type | year |
+----+------------+------------+------+
| 1 | Robotina | mechanical | 1972 |
| 2 | Astro Boy | mechanical | 1952 |
| 3 | Terminator | cyborg | 2029 |
+----+------------+------------+------+
3 rows in set (0.00 sec)
可以通过主键查找一条特定的记录,然后打印他们的名称;当记录在内存中时,可以修改其内容然后保存改变:
<?php
use Store\Toys\Robots;
$robot = Robots::findFirst(3); // Find record with id = 3
echo $robot->name; // Prints 'Terminator'
$robot->name = 'RoboCop';
$robot->save();
可以看到,不需要使用原始SQL语句。Phalcon\Mvc\Model为web应用程序提供了高度的数据库抽象。
3. 查找记录
3.1 查询记录的方法
Phalcon\Mvc\Model也提供了几种查询记录的方法。下面的例子将展示如何从一个模型中查询一条或多条记录:
<?php
use Store\Toys\Robots;
// 查找所有记录
$robots = Robots::find();
// 条件查找
$robots = Robots::find("type = 'mechanical'");
// 条件查找,结果排序
$robots = Robots::find(["type = 'virtual'", 'order' => 'name', ]);
// 条件查找,结果排序,只查找100条记录
$robots = Robots::find(["type = 'virtual'", 'order'=>'name', ‘limit'=>100,]);
// 查找第一条记录
$robot = Robots::findFirst();
’ find() ‘和’ findFirst() '方法都接受一个指定搜索条件的关联数组。
如果想通过外部数据(如用户输入)或变量数据查找记录,则必须使用捆绑参数的形式:(Binding - Parameters)。
<?php
use Store\Toys\Robots;
$condVar = 'virtual'
$robots = Robots::find([
'conditions'=>'type = ?1',
'bind' => [ 1 => $condVar, ]
]);
可用的查询选项如下表所示:
Parameter | Description | Example |
---|---|---|
conditions | 查找操作的查询条件。仅用于提取满足指定条件的记录。默认情况下Phalcon\Mvc\Model假设第一个参数是条件。 | 'conditions' => "name LIKE 'steve%'" |
columns | 返回模型中的特定列,而不是完整的列。当使用此选项时,将返回一个不完整的对象 | 'columns' => 'id, name' |
bind | Bind与选项一起使用,通过替换占位符和转义值来提高安全性 | 'bind' => ['status' => 'A', 'type' => 'some-time'] |
bindTypes | 在绑定参数时,可以使用此参数定义绑定参数的附加强制转换,从而进一步提高安全性 | 'bindTypes' => [Column::BIND_PARAM_STR, Column::BIND_PARAM_INT] |
order | 用于对结果集进行排序。使用一个或多个用逗号分隔的字段 | 'order' => 'name DESC, status' |
limit | 将查询结果限制在一定数量范围内 | 'limit' => 10 |
offset | 将查询结果偏移一定的量 | 'offset' => 5 |
group | 允许跨多个记录收集数据,并将结果按一个或多个列分组 | 'group' => 'name, status' |
for_update | 使用此选项,Phalcon\Mvc\Model读取最新可用数据,并对读取的每一行设置独占锁 | 'for_update' => true |
shared_lock | 使用此选项,Phalcon\Mvc\Model读取最新可用数据,并在读取的每一行上设置共享锁 | 'shared_lock' => true |
cache | 缓存结果集,减少对关系数据库的持续访问 | 'cache' => ['lifetime' => 3600, 'key' => 'my-find-key'] |
hydration | 设置水合策略(hydration strategy),以表示结果中的每个返回记录 | 'hydration' => Resultset::HYDRATE_OBJECTS |
3.2 面向对象的查询记录方法
如果你愿意,也有一种以面向对象的方式创建查询的方法,而不是使用参数数组:
<?php
use Store\Toys\Robots;
\$robots = Robots::query()
->where('type = :type:')
->andWhere('year < 2000')
->bind(['type' => 'mechanical'])
->order('name')
->execute();
上述代码中,静态方法’ query() '返回一个Phalcon\Mvc\Model\Criteria对象。
所有查询都在内部作为PHQL查询处理。PHQL是一种高级的、面向对象的、类似sql的语言。这种语言为您提供了更多的功能来执行查询,如加入其他模型,定义分组,添加聚合等。
最后,还有“findFirstBy()”方法。该方法扩展了前面提到的’ findFirst() '方法。它允许您通过使用方法本身中的属性名,并向它传递一个包含要在该列中搜索的数据的参数来快速执行从表中的检索。
如模型中有‘price’这个属性(表的字段),则就可以使用 ‘findFirstByPrice($price)’ 这样的方法来查询。
3.3 模型结果集
’ findFirst() ‘直接返回一个被调用类的实例(当有数据要返回时);’ find() '方法返回一个Phalcon\Mvc\Model\Resultset\Simple的对象。这是一个封装了结果集的所有功能的对象,比如遍历、查找特定记录、计数等。
这个对象比标准数组更强大。Phalcon\Mvc\Model\Resultset最大的特性之一是在内存中任何时候都只有一条记录。这对内存管理非常有帮助,特别是在处理大量数据时。
以下是对结果集进行操作的示例:
<?php
use Store\Toys\Robots;
// 获得所有记录的一个结果集
$robots = Robots::find();
// 用 foreach 进行遍历
foreach ($robots as $robot)
{
echo $robot->name, "\n";
}
// 用while进行遍历,使用指针定位结果集中的记录
$robots->rewind();
while ($robots->valid()) {
$robot = $robots->current();
echo $robot->name, "\n";
$robots->next();
}
// 统计结果集的记录条数
echo count($robots);
// 另一种方法统计结果集的记录条数
echo $robots->count();
// 将结果集的当前指针指向第三条记录(第一条记录的索引为0)
$robots->seek(2);
$robot = $robots->current();
// 通过索引号访问结果集中的记录
$robot = $robots[5];
// 判断在指定的索引位置是否存在一条记录
if (isset($robots[3])) {
$robot = $robots[3];
}
// 获取结果集的第一条记录
$robot = $robots->getFirst();
// 获取结果集的最后一条 记录
$robot = $robots->getLast();
Phalcon的结果集可滚动游标,你可以通过访问它的位置来获得任何行,或者寻找指向特定位置的内部指针。请注意,一些数据库系统不支持可滚动游标,这会迫使重新执行查询,以便将游标倒回起始位置,并在请求的位置获得记录。类似地,如果一个结果集被遍历多次,则查询必须执行相同的次数。
由于在内存中存储大型查询结果可能会消耗大量资源,因此结果集以32行的块形式从数据库中获得,从而减少了在多种情况下重新执行请求的需要。
结果集可以序列化并存储在缓存后端。Phalcon\Cache可以帮助完成这项任务。但序列化数据会导致Phalcon\Mvc\Model以数组形式从数据库中检索所有数据,从而在此过程中消耗更多内存。
<?php
// 查找获取结果集
$parts = Parts::find();
// 将结果集序列化后存储为文件
file_put_contents('cache.txt', serialize($parts));
// 从文件中读取数据后,反序列化为结果集对象
$parts = unserialize(file_get_contents('cache.txt'));
3.4 自定义结果集
在从数据库检索数据时,应用程序的逻辑有时需要对数据进行额外的操作。以前实现这种需求时,我们只能扩展模型,增加一个方法,给调用者返回一个转换数据后的数组。
使用自定义结果集,您不再需要这样做。自定义结果集将封装原本在模型中并可被其他模型重用的功能,从而保持代码DRY。这样,’ find() ‘方法将不再返回默认的Phalcon\Mvc\Model\Resultset,而是返回自定义的[Phalcon\Mvc\Model\Resultset]。Phalcon允许你通过在你的模型中使用’ getResultsetClass() '来做到这一点。
- 第1步:从结果集类上扩展一个自定义的结果集类;
- 第2步:在模型中,修改
getResultsetClass()
方法,设置自定义的结果集;
<?php
public function getResultsetClass() {
return 'Application\Mvc\Model\Resultset\Custom';
}
- 最后,在自己的代码中使用自定义结果集类中,自定义的各种数据处理的方法
3.5 过滤结果集
过滤数据最有效的方法是设置一些搜索条件,数据库将使用表上设置的索引来更快地返回数据。Phalcon还允许您使用PHP中那些数据库中没有的资源来过滤数据。
<?php
$customers = Customers::find();
// 以下过滤器定义了一个匿名函数,其功能是只返回电子邮件有效的用户
$customers = $customers->filter(
function($customer)
{
if (filter_var($customer->email, FILTER_VALIDATE_EMAIL)) {
return $customer;
}
}
);
3.6 捆绑参数
Phalcon\Mvc\Model中支持绑定参数。建议您使用这种方法,以消除代码受到SQL注入攻击的可能性。字符串和整数占位符都支持。绑定参数可以简单地实现如下:
<?php
use Store\Toys\Robots;
// 查询时捆绑字符串占位符,参数的键和占位符一致
$robots = Robots::find([
'name = :name: AND type = :type:',
'bind' => ['name' => 'Robotina', 'type'=>'maid', ],
]);
// 查询时捆绑整数占位符
$robots = Robots::find([
'name = ?1 AND type = ?2',
'bind' => [1 => 'Robotina', 2 => 'maid', ],
]);
// 查询时,同时捆绑字符串占位符和整数占位符
$robots = Robots::find([
'name = :name: AND type = ?1',
'bind'=> ['name'=>'Robotina', 1 => 'maid', ],
]);
当使用数字占位符时,需要将它们定义为整数’1’或’2’。使用双引号的"1”或“2”被认为是字符串而不是数字,占位符不能被成功替换。
字符串使用PDO自动转义。该函数考虑了连接字符集,因此一定要在连接参数或数据库配置中定义正确的字符集,因为错误的字符集将在存储或检索数据时产生不良影响。
此外,可以设置参数’ bindTypes ',这允许根据它的数据类型定义参数应该如何绑定:
<?php
use Phalcon\Db\Column;
use Store\Toys\Robots;
// 捆绑参数数组
$parameters = [
'name' => 'Robotina',
'year' => 2008,
];
// 参数类型定义数组
$types = [
'name' => Column::BIND_PARAM_STR,
'year' => Column::BIND_PARAM_INT,
];
// 查询时,使用字符串占位符
$robots = Robots::find(
[
'name = :name: AND year = :year:',
'bind' => $parameters,
'bindTypes' => $types,
]
);
由于默认的绑定类型是’ Phalcon\Db\Column::BIND_PARAM_STR ‘,如果所有的列都是该类型,则不需要指定’ bindTypes '参数。如果你在绑定参数中使用绑定数组,请记住键必须从0开始编号:
绑定参数可用于所有查询方法,如“find()”和“findFirst()”,但也可用于计算方法,如“count()”,“sum()”,“average()”等。
4. 初始化和准备获取后的记录
在从数据库获取记录后,可能需要在应用程序的其余部分使用该数据之前初始化该数据。可以在一个模型中实现’ afterFetch()'方法,这个事件将在创建实例,并将数据分配给它之后自动被执行。
在调用save()方法的前后,phalcon会自动调用 beforeSave() 和 afterSave() 方法,如果这两个方法存在的话;这两个方法可以被用于数据初始化和数据后处理。
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $id;
public $name;
public $status;
// 初始化数据方法1:此方法将在创建实例并获取数据后被自动调用执行
public function afterFetch()
{
$this->status = explode(',', $this->status);
}
// 初始化数据方法2:在Getter方法中进行处理
public function getStatus()
{
return explode(',', $this->status);
}
}
5. 生成计算
计算(或聚合)是数据库系统常用函数的助手,如’ COUNT ', ’ SUM ', ’ MAX ', ’ MIN ‘或’ AVG '。Phalcon\Mvc\Model允许直接从公开的方法使用这些函数。
6. 创建和更新记录
create和update方法在内部调用了save方法。有必要在实体中正确定义一个主键,以确定是否应该更新或创建记录。
此外,save方法还执行模型中定义的相关验证器、虚拟外键和事件:
可以将数组传递给“save”以避免手动分配每一列。Phalcon\Mvc\Model将检查数组中传递的列是否实现了setter,并给予它们优先级,而不是直接分配属性的值:
<?php
use Store\Toys\Robots;
$robot = new Robots();
$robot->save(
[
'type' => 'mechanical',
'name' => 'Astro Boy',
'year' => 1952,
]
);
直接分配的值或通过属性数组分配的值将根据相关属性数据类型进行转义/清除。所以你可以传递一个不安全的数组,而不用担心可能的SQL注入:
<?php
use Store\Toys\Robots;
$robot = new Robots();
// 只有当您希望允许用户插入/更新模型中的每一列时,才使用如下的形式保存
$robot->save($_POST);
// 也通过设置一个字段白名单来控制输入
$robot->save(
$_POST,
[
'name',
'type',
]
当一个应用程序有很多地方会同时修改数据库表时,使用’ Phalcon\Mvc\Model::save() ‘可能会出现冲突的情况(如创建一条已经存在的记录)。如果想要绝对确保一条记录被创建或更新,可以用’ create() ‘或’ update() ‘来替代’ save() '方法。
在调用create()方法的前后,phalcon会自动调用 beforeCreate() 和 afterCreate() 方法,如果这两个方法存在的话;
在调用update()方法的前后,phalcon会自动调用 beforeUpdate() 和 afterUpdate() 方法,如果这两个方法存在的话;
7. 删除记录
使用 Phalcon\Mvc\Model::delete()
方法来删除一条记录。
以下事件可用于定义可在执行删除操作时执行的自定义业务规则:
Operation | Name | Can stop operation? | Explanation |
---|---|---|---|
Deleting | afterDelete | No | 删除操作被执行后所做的处理 |
Deleting | beforeDelete | Yes | 删除操作之前所做的处理 |
8. 水合模式(Hydration Modes)
如前所述,结果集是完整对象的集合,这意味着每个返回的结果都是表示数据库中一行的对象。这些对象可以被修改并再次保存到持久性:
有时获取记录只是为了以只读模式显示给用户,在这种情况下,改变记录的表示方式以方便处理可能是有用的。用于表示结果集中返回的对象的策略称为“水合模式”:
<?php
use Phalcon\Mvc\Model\Resultset;
use Store\Toys\Robots;
$robots = Robots::find();
// 设置水合模式为数组形式,查询结果以数组形式展现
$robots->setHydrateMode(
Resultset::HYDRATE_ARRAYS
);
foreach ($robots as $robot) {
echo $robot['year'], PHP_EOL;
}
// 设置水合模式为对象形式,查询结果以标准类的形式展现
$robots->setHydrateMode(
Resultset::HYDRATE_OBJECTS
);
foreach ($robots as $robot) {
echo $robot->year, PHP_EOL;
}
// R设置水合模式为记录形式(默认模式),查询结果的每条记录都是一个实例
$robots->setHydrateMode(
Resultset::HYDRATE_RECORDS
);
foreach ($robots as $robot) {
echo $robot->year, PHP_EOL;
}
可以在find方法的参数中,指定水合模式:
$robots = Robots::find(['hydration' => Resultset::HYDRATE_ARRAYS,]);
9. 表的前缀
如果你想让你所有的表都有特定的前缀,同时在所有模型中都不设置源,你可以使用’ Phalcon\Mvc\Model\Manager ‘类和方法’ setModelPrefix() ':
<?php
use Phalcon\Mvc\Model\Manager;
use Phalcon\Mvc\Model;
class Robots extends Model
{
}
$manager = new Manager();
$manager->setModelPrefix('wp_');
$robots = new Robots(null, null, $manager);
echo $robots->getSource(); // 将返回 wp_robots
10. 自动生成ID列
有些模型可能有ID列,ID列通常是表的主键。
Phalcon\Mvc\Model可以识别ID列,并在生成的’ INSERT 'SQL语句中省略ID列。创建一个记录之后,数据库系统将自动为ID列生成一个值。
<?php
$robot->save();
echo 'The generated id is: ', $robot->id;
根据数据库系统的不同,自动生成的ID列可能是PostgreSQL中的串行列,也可能是MySQL中的auto_increment列。
PostgreSQL使用序列来生成自动数字值,默认情况下,Phalcon尝试从序列’ table_field_seq ‘中获取生成的值,如果该序列有不同的名称,则需要实现’ getSequenceName() '方法:
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function getSequenceName()
{
return 'robots_sequence_name';
}
}
11. 跳过的列
设置跳过的列告诉Phalcon\Mvc\Model,在创建和/或更新记录时要忽略一些字段,以便使得数据库系统分配值时不触发警告。
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
// 设置在 INSERT 或 UPDATE 操作时,要忽略的列
$this->skipAttributes(['year', 'price',]);
// 设置仅在 INSERT 操作时要忽略的列
$this->skipAttributesOnCreate(['created_at',]);
// 设置仅在 UPDATE 操作时要忽略的列
$this->skipAttributesOnUpdate(['modified_in',]);
}
}
上述语句将在整个应用程序的每个’ INSERT ’ / ’ UPDATE ‘操作中全局忽略这些字段。如果想在不同的’INSERT’/'UPDATE’操作中忽略不同的属性,可以对上述方法的第二个参数(bool值)进行替换。
可以对将被跳过的属性进行赋值,从而使其其获得默认值。
也可以在 ‘beforeCreate’ 方法中,定义属性的默认值:
注意:永远不要使用Phalcon\Db\RawValue来分配外部数据(例如用户输入)或变量数据。在将参数绑定到查询时,这些字段的值将被忽略。因此,它可以用来攻击注入SQL的应用程序。
12. 动态更新
默认情况下,SQL ’ UPDATE '语句是与模型中定义的每一列一起创建的(完整的全字段SQL更新)。您可以将更改模型来进行动态更新,在本例中,只使用已更改的字段来创建最终的SQL语句。
这样做可以通过减少应用程序和数据库服务器之间的流量来提高性能,特别是当表中有blob/text字段时。
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->useDynamicUpdate(true);
}
}
13. 独立的字段映射
ORM支持独立的列映射,这允许在模型中使用与表中不同的列名。Phalcon将识别新的列名,并相应地重命名它们,以匹配数据库中各自的列。当需要重命名数据库中的字段而不必担心代码中的所有查询时,这是一个很好的特性,只要对模型中的列映射进行更改就可以解决问题。例如:
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $code;
public $theName;
public $theType;
public $theYear;
public function columnMap()
{
// 数组的每个元素中,键是表中的真实字段名,值是这个字段在应用中的名称
return [
'id' => 'code',
'the_name' => 'theName',
'the_type' => 'theType',
'the_year' => 'theYear',
];
}
}
定义了字段映射后,就可以在代码中使用新名称来替代原字段名称了。
在重命名列时考虑以下几点:
- 对关系/验证器中的属性的引用必须使用新名称
- 引用真实列名将导致ORM异常
独立列映射允许您:
- 使用您自己的约定编写应用程序
- 在代码中消除供应商前缀/后缀
- 更改列名而不更改应用程序代码
14. 记录快照
可以在查询时维护一个记录的快照。能用这个特性来实现审计(核对数据一致性),或者根据从数据库查询的结果,来确知道哪些字段被更改了。
当激活这个特性时,应用程序会消耗更多的内存来记录从数据库获得的原始值。
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->keepSnapshots(true);
}
}
// 从数据库中获取一条记录
$robot = Robots::findFirst();
// 改变一个字段的值
$robot->name = 'Other name';
// 调用方法来查询被改变的字段,或者判断某个字段是否已改变
var_dump($robot->getChangedFields()); // ['name']
var_dump($robot->hasChanged('name')); // true
var_dump($robot->hasChanged('type')); // false
快照在模型 creation/update 时更新。使用’ hasUpdated() ‘和’ getUpdatedFields() ‘可以用来检查字段是否在创建/保存/更新后更新,但如果在’ afterUpdate() ', ’ afterSave() ‘或’ afterCreate() ‘中执行’ getChangedFields() ',则可能会对应用程序造成问题。
您可以使用以下命令禁用此功能:
Phalcon\Mvc\Model::setup(['updateSnapshotOnSave' => false,]);
或者可以在php.ini
中做统一的设置:
phalcon.orm.update_snapshot_on_save = 0
使用此功能将产生以下效果:
’ getUpdatedFields() '将正确返回更新的字段,或者如上所述,您可以通过设置相关的ini值返回到前面的行为。
15. 指向一个不同的模式
如果要将模型映射到一个非默认的数据库中的表,可以使用’ setSchema() '方法来定义:
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
// 类名默认是此模型将映射到‘robots’表,以下语句将此模型的映射表改为’toys’
$this->setSchema('toys');
}
}
16. 设置多个数据库
在Phalcon中,所有模型可以属于同一个数据库连接,也可以有一个单独的数据库连接。
实际上,当Phalcon\Mvc\Model需要连接到数据库时,它将请求应用程序的服务容器中的’ db ‘服务。你可以在’ initialize() '方法中覆盖这个服务。
<?php
use Phalcon\Db\Adapter\Pdo\Mysql as MysqlPdo;
use Phalcon\Db\Adapter\Pdo\PostgreSQL as PostgreSQLPdo;
// 这个DI服务将返回一个 MySQL 数据库
$di->set(
'dbMysql',
function () {
return new MysqlPdo(
[
'host' => 'localhost',
'username' => 'root',
'password' => 'secret',
'dbname' => 'invo',
]
);
}
);
// 这个DI服务返回一个 PostgreSQL 数据库
$di->set(
'dbPostgres',
function () {
return new PostgreSQLPdo(
[
'host' => 'localhost',
'username' => 'postgres',
'password' => '',
'dbname' => 'invo',
]
);
}
);
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->setConnectionService('dbPostgres');
}
}
Phalcon提供了更大的灵活性,可以定义必须用于“读”和“写”的连接。这对于平衡实现主从架构的数据库的负载特别有用。
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->setReadConnectionService('dbSlave');
$this->setWriteConnectionService('dbMaster');
}
}
ORM还提供了水平分区功能,允许你根据当前查询条件实现一个“分区”选择:
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
/**
* 动态选择一个读数据的连接分区
*/
public function selectReadConnection($intermediate, $bindParams, $bindTypes)
{
// 检查Select语句中是否有‘where’关键字
if (isset($intermediate['where'])) {
$conditions = $intermediate['where'];
// 根据条件选择分区
if ($conditions['left']['name'] === 'id') {
$id = $conditions['right']['value'];
if ($id > 0 && $id < 10000) {
return $this->getDI()->get('dbShard1');
}
if ($id > 10000) {
return $this->getDI()->get('dbShard2');
}
}
}
// 没有where条件,使用默认分区
return $this->getDI()->get('dbShard0');
}
}
’ selectReadConnection() '方法被调用来选择正确的连接,该方法拦截任何执行的新查询:
17. 向模型注入服务
你可能需要访问模型中的应用程序服务,下面的例子解释了如何做到这一点:
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function notSaved()
{
// 从DI中获取闪存服务
$flash = $this->getDI()->getFlash();
$messages = $this->getMessages();
// 使用闪存服务中的错误处理方法
foreach ($messages as $message) {
$flash->error($message);
}
}
}
每次“创建”或“更新”操作失败时都会触发“notSaved”事件。上述代码中,如果保存失败,则从DI容器中获取’ flash '服务,进行错误消息的验证。
18. 禁用/启用相关特性
在ORM中,我们实现了一种机制,允许您在运行中全局启用/禁用特定的特性或选项。根据您使用ORM的方式,您可以禁用不使用的ORM。如果需要,也可以暂时禁用这些选项。
<?php
use Phalcon\Mvc\Model;
Model::setup(
[
'events' => false,
'columnRenaming' => false,
]
);
可用的选项如下:
选项 | 描述 | 缺省值 |
---|---|---|
astCache | 启用/禁用所有模型的回调、钩子和事件通知 | null |
cacheLevel | 设置ORM的缓存级别 | 3 |
castOnHydrate | 启用/禁用水合模式 | false |
columnRenaming | 启用/禁用列重命名 | true |
disableAssignSetters | 允许在模型中禁用setter | false |
enableImplicitJoins | 允许隐式的合并 | true |
enableLiterals | true | |
escapeIdentifiers | true | |
events | 启用/禁用所有模型的回调、钩子和事件通知 | true |
exceptionOnFailedSave | 启用/禁用在’ save() '失败时抛出异常 | false |
forceCasting | false | |
ignoreUnknownColumns | 启用/禁用忽略模型上的未知列 | false |
lateStateBinding | 启用/禁用’ Phalcon\Mvc\Model::cloneResultMap() '方法的后期状态绑定 | false |
notNullValidations | ORM自动验证映射表中出现的非空列 | true |
parserCache | null | |
phqlLiterals | 启用/禁用PHQL解析器中的文字 | true |
uniqueCacheId | 3 | |
updateSnapshotOnSave | 启用/禁用更新快照save() | true |
virtualForeignKeys | 启用/禁用虚拟外键 | true |
注意:assign()方法总是使用setter,如果它们存在时将总是传递数据参数。这将为应用程序增加一些额外的开销。可以通过在php.ini文件中 添加 phalcon.orm.disable_assign_setters = 1
来改变这种行为。添加之后,它将只是简单地使用$this->property = value
来做设置,而不是使用setter()方法。
19. 独立模式的组件
在独立模式下使用Phalcon\Mvc\Model示例如下:
<?php
use Phalcon\Di;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Manager as ModelsManager;
use Phalcon\Db\Adapter\Pdo\Sqlite as Connection;
use Phalcon\Mvc\Model\Metadata\Memory as MetaData;
$di = new Di();
// 设置一个连接
$di->set('db', new Connection(['dbname' => 'sample.db',]));
// 设置一个模型管理器
$di->set('modelsManager', new ModelsManager());
//使用内存的元数据适配器或其他
$di->set('modelsMetadata', new MetaData());
// 创建一个模型
class Robots extends Model
{
}
// 使用模型
echo Robots::count();