模型表示应用程序信息(数据)以及这些数据的处理规则,主要用于管理与对应数据表的交互规则。大多数状况下,数据库中的每一张表都有对应的模型。应用程序中的大部分业务逻辑集中在模型中。php
Phalcon应用中,Phalcon\Mvc\Model
是全部模型的基类。它提供了数据库独立、基础CRUD、高级查找、模型关联以及其余服务。mysql
Phalcon\Mvc\Model
将调用的方法动态转换为相应的数据库操做,避免了直接使用SQL。web
模型使用数据库高级抽象层,若是想要使用更为底层的方式操做数据库,请参考Phalcon\Db
组件文档。sql
模型需继承Phalcon\Mvc\Model
类,以大驼峰格式命名。数据库
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class RobotParts extends Model { }
若是使用PHP 5.四、5.5版本,建议在模型中声明对应数据表的全部字段,以节约内存。数组
模型Store\Toys\RobotParts
默认映射robot_parts
表,能够调用setSource()
方法手动指定映射表:缓存
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class RobotParts extends Model { public function initialize() { $this->setSource('toys_robot_parts'); } }
模型RobotParts
如今映射toys_robot_parts
表。initialize()
方法有助于在模型中建立自定义行为,如为模型指定映射表。安全
initialize()
方法在请求期间只调用一次,目的是为该模型的全部实例执行初始化操做。若是每次实例化模型的时候都须要进行初始化,可使用onConstruct()
方法:架构
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class RobotParts extends Model { public function onConstruct() { // ... } }
模型能够定义公共属性,在任何获取了模型实例的地方均可以读写模型的公共属性:并发
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public $price; }
另外一种实现方式是getters
和setters
方法,控制哪些模型属性能够公开访问。这种方式的好处是,开发者能够在对模型属性进行写操做时执行转换和验证,这是使用公共属性方式没法实现的。此外,getters
和setters
能够在不改动模型和接口的前提下,应对将来可能的改动。若是字段名称改变,惟一须要的改动的地方是getters
和setters
中引用的模型私有属性。
<?php namespace Store\Toys; use InvalidArgumentException; use Phalcon\Mvc\Model; class Robots extends Model { protected $id; protected $name; protected $price; public function getId() { return $this->id; } public function setName($name) { // name不能过短 if (strlen($name) < 10) { throw new InvalidArgumentException( 'The name is too short' ); } $this->name = $name; } public function getName() { return $this->name; } public function setPrice($price) { // price不能为负 if ($price < 0) { throw new InvalidArgumentException( "Price can't be negative" ); } $this->price = $price; } public function getPrice() { // 返回前,将该值转换为double类型 return (double) $this->price; } }
虽然公共属性在开发中的复杂度更低,可是getters
和setters
能够大大提升程序的测试性、扩展性和可维护性。开发者能够根据需求决定哪一种方式更适合他们的应用。ORM兼容这两种方式。
使用getters
和setters
时,属性名中的下划线可能会致使问题。
若是在属性名中使用下划线,在声明getters
和setters
魔术方法时,仍然要使用驼峰格式($model->getPropertyName()
代替$model->getProperty_name()
,$model->findByPropertyName()
代替$model->findByProperty_name()
等)。大多数系统推荐驼峰写法,而不是下划线写法,因此建议按照文档中的写法为属性命名。可使用字段映射(如上所述)以确保属性正确映射到数据表中对应字段。
模型的每个实例表明数据表中的一条记录,能够经过读取模型对象属性来访问记录数据。例如,表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; // 查找id = 3的记录 $robot = Robots::findFirst(3); // 输出'Terminator' echo $robot->name;
一旦记录存储在内存中,就能够修改其中的数据并保存:
<?php use Store\Toys\Robots; $robot = Robots::findFirst(3); $robot->name = 'RoboCop'; $robot->save();
Phalcon\Mvc\Model
为web应用提供了数据库高级抽象层,不须要使用原生SQL语句。
Phalcon\Mvc\Model
提供了多种查询记录的方法,下面例子演示如何用模型查找一条或多条记录:
<?php use Store\Toys\Robots; // 查询全部记录 $robots = Robots::find(); echo 'There are ', count($robots), "\n"; // 查询type = 'mechanical'的记录 $robots = Robots::find("type = 'mechanical'"); echo 'There are ', count($robots), "\n"; // 获取type = 'virtual'的记录,根据name排序 $robots = Robots::find( [ "type = 'virtual'", 'order' => 'name', ] ); foreach ($robots as $robot) { echo $robot->name, "\n"; } // 获取type = 'virtual'的前100条记录,根据name排序 $robots = Robots::find( [ "type = 'virtual'", 'order' => 'name', 'limit' => 100, ] ); foreach ($robots as $robot) { echo $robot->name, "\n"; }
若是要使用外部数据(如用户输入)或变量查找记录,必须进行参数绑定。
使用findFirst()
方法,获取知足给定条件的第一条记录:
<?php use Store\Toys\Robots; // 获取robots表的第一条记录 $robot = Robots::findFirst(); echo 'The robot name is ', $robot->name, "\n"; // 获取robots表中type = 'mechanical'的第一条记录 $robot = Robots::findFirst("type = 'mechanical'"); echo 'The first mechanical robot name is ', $robot->name, "\n"; // 获取robots表中type = 'virtual'的第一条记录,根据name排序 $robot = Robots::findFirst( [ "type = 'virtual'", 'order' => 'name', ] ); echo 'The first virtual robot name is ', $robot->name, "\n";
find()
方法和findFirst()
方法都接受一个包含指定搜索条件的关联数组:
<?php use Store\Toys\Robots; $robots = Robots::findFirst( [ "type = 'virtual'", 'order' => 'name DESC', 'limit' => 30, ] ); $robots = Robots::find( [ 'conditions' => 'type = ?1', 'bind' => [ 1 => 'virtual', ], ] );
有下列查询选项:
参数 | 说明 | 示例 |
---|---|---|
conditions |
查询操做的搜索条件,用于筛选符合指定条件的记录。默认状况下,PhalconMvcModel 假定第一个参数就是搜索条件 |
'conditions' => "name LIKE 'steve%'" |
columns |
获取模型中的指定字段,而不是全部字段。使用此选项时,返回不完整对象。 | 'columns' => 'id, name' |
bind |
参数绑定与conditions 一块儿使用,经过替换占位符、转义特殊字符从而提升安全性 |
'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 |
使用此选项,PhalconMvcModel 将读取最新的可用数据,并为读取到的每一条记录设置独占锁 |
'for_update' => true |
shared_lock |
使用此选项,PhalconMvcModel 将读取最新的可用数据,并为读取到的每一条记录设置共享锁 |
'shared_lock' => true |
cache |
缓存结果集,减小对数据库的持续访问 | 'cache' => ['lifetime' => 3600, 'key' => 'my-find-key'] |
hydration |
设置结果集返回模式 | 'hydration' => Resultset::HYDRATE_OBJECTS |
除了使用参数数组,还可使用面向对象的方式建立查询:
<?php use Store\Toys\Robots; $robots = Robots::query() ->where('type = :type:') ->addWhere('year < 2000') ->bind(['type' => 'machanical']) ->order('name') ->execute();
静态方法query()
返回一个IDE自动完成友好的Phalcon\Mvc\Model\Criteria
对象。
全部查询在内部都以PHQL查询的方式处理。PHQL是一种高级的、面向对象的类SQL语言,这种语言提供了多种功能来执行查询,如join其余模型,定义分组,添加聚合等。
最后,还有一个findFirstBy<property-name>()
方法,该方法扩展了findFirst()
方法,它容许经过使用方法中的属性名称并向它传递一个包含要在该字段中搜索数据的参数,从表中快速执行检索。以上面的Robots模型为例:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public $price; }
这里有三个属性:$id
,$name
和$price
,假设要检索name为'Terminator'的第一条记录,代码以下:
<?php use Store\Toys\Robots; $name = 'Terminator'; $robot = Robots::findFirstByName($name); if ($robot) { echo 'The first robot with the name ', $name . ' cost ' . $robot->price, '.'; } else { echo 'There were no robots found in our table with the name ' . $name . '.'; }
请注意,咱们在调用的方法中使用了Name并传递了变量$name
,表中name字段值为$name
的记录就是咱们要查找的。
findFirst()
方法返回被调用类的实例(若是有结果返回),而find()
方法返回Phalcon\Mvc\Model\Resultsets\Simple
对象,该对象封装告终果集应有的全部功能,如遍历,查找特定记录,统计等。
这些对象比通常数组功能强大,Phalcon\Mvc\Model\Resultset
一个最大的特色是,任什么时候刻,只有一条记录保存在内存中。这对内存管理有极大帮助,特别是在处理大批量数据时。
<?php use Store\Toys\Robots; // 获取全部记录 $robots = Robots::find(); // foreach遍历 foreach ($robots as $robot) { echo $robot->name, "\n"; } // while遍历 while ($robots->valid()) { $robot = $robots->current(); echo $robot->name, "\n"; $robots->next(); } // 结果集计数 echo count($robots); // 另外一种结果集计数方法 echo $robots->count(); // 移动游标到第三条记录 $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 = Parts::find(); // 将结果集保存到文件中 file_put_contents( 'cache.txt', serialize($parts) ); // 从文件中获取结果集 $parts = unserialize( file_get_contents('cache.txt') ); // 遍历 foreach ($parts as $part) { echo $part->id; }
有时候应用程序须要对数据库中检索到的数据进行额外处理。之前,咱们只须要扩展模型或将功能封装在模型或trait当中,而后返回一组通过转换的数据。
经过自定义结果集,不须要再如此处理。自定义结果集将封装本来封装在模型中并能被其余模型重用的功能,这样能够简化代码。如此,find()
方法返回自定义对象,而再也不返回Phalcon\Mvc\Model\Resultset
对象。Phalcon容许在模型中定义getResultsetClass()
方法来实现此操做。
首先,声明resultset类:
<?php namespace Application\Mvc\Model\Resultset; use Phalcon\Mvc\Model\Resultset\Simple; class Custom extends Simple { public function getSomeData() { /** CODE */ } }
模型中,在getResultsetClass()
方法里设置resultset类,以下:
<?php namespace Phalcon\Test\Models\Statistics; use Phalcon\Mvc\Model; class Robots extends Model { public function getSource() { return 'robots'; } public function getResultsetClass() { return 'Application\Mvc\Model\Resultset\Custom'; } }
最后,代码里应包含以下内容:
<?php /** * 查找robots表记录 */ $robots = Robots::find( [ 'conditions' => 'date between "2017-01-01" AND "2017-12-31"', 'order' => 'date', ] ); /** * 传递数据到视图 */ $this->view->mydata = $robots->getSomeData();
过滤数据最有效的方法之一是设置搜索条件,数据库将使用表索引以更快的返回数据。Phalcon容许使用PHP函数或是数据库不支持的方式过滤数据:
<?php $customers = Customers::find(); $customers = $customers->filter( function ($customer) { // 只返回e-mail合法的记录 if (filter_var($customer->email, FILTER_VALIDATE_EMAIL)) { return $customer; } } );
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开始的索引数组:
<?php use Store\Toys\Robots; $array = ['a', 'b', 'c']; // $array: [[0] => 'a', [1] => 'b', [2] => 'c'] unset($array[1]); // $array: [[0] => 'a', [2] => 'c'] // 如今必须重建数组索引 $array = array_values($array); // $array: [[0] => 'a', [1] => 'c'] $robots = Robots::find( [ 'letter IN ({letter:array})', 'bind' => [ 'letter' => $array, ], ] );
参数绑定除了可用于全部查询方法,如find()
和findFirst()
外,还可用于count()
,sum()
,average()
等统计方法。
使用finders
时,会自动使用参数绑定:
<?php use Store\Toys\Robots; // 显式使用参数绑定 $robots = Robots::find( [ 'name = ?0', 'bind' => [ 'Ultron', ], ] ); // 隐式使用参数绑定 $robots = Robots::findByName('Ultron');
有时从数据库获取记录以后,在数据被应用程序使用以前,须要对数据进行初始化。能够在模型中实现afterFetch()
方法,实例化模型时会执行该方法,并将数据传递给它:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public $status; public function beforeSave() { // 将数组转换成字符串 $this->status = join(',', $this->status); } public function afterFetch() { // 将字符串转换成数组 $this->status = explode(',', $this->status); } public function afterSave() { // 将字符串转换成数组 $this->status = explode(',', $this->status); } }
若是使用getters/setters
代替公共属性,或同时使用它们,能够在字段被访问时初始化字段:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public $status; public function getStatus() { return explode(',', $this->status); } }
运算(或聚合)是数据库中经常使用的辅助方法,如COUNT
,SUM
,MAX
,MIN
和AVG
。Phalcon\Mvc\Model
能够直接使用这些方法。
Count示例:
<?php // 表employees总记录数 $rowcount = Employees::count(); // 表employees共有多少不一样areas值 $rowcount = Employees::count( [ 'distinct' => 'area', ] ); // 表employees共有多少area为'Testing'的记录 $rowcount = Employees::count( 'area = "Testing"' ); // 按area分组统计表employees记录 $group = Employees::count( [ 'group' => 'area', ] ); foreach ($group as $row) { echo 'There are ', $row->rowcount, ' in ', $row->area; } // 按area分组统计表employees记录,并根据数目排序 $group = Employees::count( [ 'group' => 'area', 'order' => 'rowcount', ] ); // 使用参数绑定避免SQL注入 $group = Employees::count( [ 'type > ?0', 'bind' => [ $type, ], ] );
Sum示例:
<?php // 全部employees的salaries总和 $total = Employees::sum( [ 'column' => 'salary', ] ); // area = 'Sales'的全部employees的salaries总和 $total = Employees::sum( [ 'column' => 'salary', 'conditions' => 'area = "Sales"', ] ); // 根据area分组统计salaries $group = Employees::sum( [ 'column' => 'salary', 'group' => 'area', ] ); foreach ($group as $row) { echo 'The sum of salaries of the ', $row->area, ' is ', $row->sumatory; } // 根据area分组统计salaries,salaries由高到低排序 $group = Employees::sum( [ 'column' => 'salary', 'group' => 'area', 'order' => 'sumatory DESC', ] ); // 使用参数绑定避免参数绑定 $group = Employees::sum( [ 'conditions' => 'area > ?0', 'bind' => [ $area, ], ] );
Average示例:
<?php // 全部employees的平均salary $average = Employees::average( [ 'column' => 'salary', ] ); // area = 'Sales'的employees的平均salary $average = Employees::average( [ 'column' => 'salary', 'conditions' => 'area = "Sales"', ] ); // 使用参数绑定避免SQL注 $average = Employees::average( [ 'column' => 'age', 'conditions' => 'area > ?0', 'bind' => [ $area, ], ] );
Max / Min示例:
<?php // 全部employees中age最大的 $age = Employees::maximum( [ 'column' => 'age', ] ); // area = 'Sales'的employees中age最大的 $age = Employees::maximum( [ 'column' => 'age', 'conditions' => 'area = "Sales"', ] ); // 全部employees中salary最低的 $salary = Employees::minimum( [ 'column' => 'salary', ] );
Phalcon\Mvc\Model::save()
方法会根据记录是否存在于模型映射表中而建立 / 更新记录,Phalcon\Mvc\Model
的建立和更新方法会在内部调用该方法。为此,必须在实体中正肯定义主键,以肯定是建立记录仍是更新记录。
该方法会执行相关验证器,虚拟外键和模型中定义的事件:
<?php use Store\Toys\Robots; $robot = new Robots(); $robot->type = 'mechanical'; $robot->name = 'Astro Boy'; $robot->year = 1952; if ($robot->save() === false) { echo "Umh, We can't store robots right now: \n"; $messages = $robot->getMessages(); foreach ($messages as $message) { echo $message, "\n"; } } else { echo 'Great, a new robot was saved successfully!'; }
直接传递或者经过属性数组传递的值会根据其数据类型自动被转义 / 过滤,因此能够传递一个不安全的数组而不用担忧SQL注入:
<?php use Store\Toys\Robots; $robot = new Robots(); $robot->save($_POST);
毫无防御的批量传值可能会容许攻击者设置任意字段的值,仅在容许用户插入 / 更新模型中全部字段的状况下使用上述功能,即便这些字段不是使用表单提交的。
能够在save()
方法中设置额外参数,以设置批量传值时,执行插入 / 更新操做的白名单字段。
<?php use Store\Toys\Robots; $robot = new Robots(); $robot->save( $_POST, [ 'name', 'type', ] );
应用程序高并发时,建立记录操做可能会变成更新操做。使用Phalcon\Mvc\Model::save()
方法保存记录时,可能发生这种状况。若是想确保执行建立或更新,可使用create()
和update()
方法替换save()
:
<?php use Store\Toys\Robots; $robot = new Robots(); $robot->type = 'mechanical'; $robot->name = 'Astro Boy'; $robot->year = 1952; // 仅建立记录 if ($robot->create() === false) { echo "Umh, We can't store robots right now: \n"; $messages = $robot->getMessages(); foreach ($messages as $message) { echo $message, "\n"; } } else { echo 'Great, a new robot was created successfully!'; }
create()
方法和update()
方法一样接受一个数组做为参数。
Phalcon\Mvc\Model::delete()
方法容许删除记录,使用示例:
<?php use Store\Toys\Robots; $robot = Robots::findFirst(11); if ($robot !== false) { if ($robot->delete() === false) { echo "Sorry, we can't delete the robot right now: \n"; $messages = $robot->getMessages(); foreach ($messages as $message) { echo $message, "\n"; } } else { echo 'The robots was deleted successfully!'; } }
也能够经过使用foreach遍历结果集来删除多条记录:
<?php use Store\Toys\Robots; $robots = Robots::find( "type = 'mechanical'" ); foreach ($robots as $robot) { if ($robot->delete() === false) { echo "Sorry, we can't delete the robot right now: \n"; $messages = $robot->getMessages(); foreach ($messages as $message) { echo $message, "\n"; } } else { echo 'The robot was deleted successfully!'; } }
如下事件能够用于定义在执行删除操做时,要执行的自定义业务规则:
操做 | 事件名称 | 可否终止操做 | 说明 |
---|---|---|---|
删除 | afterDelete | 否 | 删除操做后执行 |
删除 | beforeDelete | 是 | 删除操做前执行 |
经过上述事件,能够在模型中定义业务规则:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function beforeDelete() { if ($this->status === 'A') { echo "The robot is active, it can't be deleted"; return false; } return true; } }
如前所述,结果集是完整对象的集合,这意味着每条返回结果都是一个对象,表明数据表中的一行。这些对象能够修改并永久保存:
<?php use Store\Toys\Robots; $robots = Robots::find(); // 操做完整对象结果集 foreach ($robots as $robot) { $robot->year = 2000; $robot->save(); }
有时记录只能以只读模式呈现给用户,这种状况下,改变记录的展示方式有助于用户处理数据。用于表示结果集中返回的对象的策略称为'hydration mode':
<?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; } // 返回stdClass对象 $robots->setHydrateMode( Resultset::HYDRATE_OBJECTS ); foreach ($robots as $robot) { echo $robot->year, PHP_EOL; } // 返回模型实例 $robots->setHydrateMode( Resultset::HYDRATE_RECORDS ); foreach ($robots as $robot) { echo $robot->year, PHP_EOL; }
Hydration mode也能够做为find()
方法的参数传递:
<?php use Phalcon\Mvc\Model\Resultset; use Store\Toys\Robots; $robots = Robots::find( [ 'hydration' => Resultset::HYDRATE_ARRAYS, ] ); foreach ($robots as $robot) { echo $robot['year'], PHP_EOL; }
若是但愿全部表名称都有特定前缀,而且不想在每一个模型中都调用setSource()
方法,则能够调用Phalcon\Mvc\Model\Manager
的setModelprefix()
方法:
<?php use Phalcon\Mvc\Model; use Phalcon\Mvc\Model\Manager; class Robots extends Model { } $manager = new Manager(); $manager->setModelPrefix('wp_'); $robots = new Robots(null, null, $manager); echo $robots->getSource(); // 返回wp_robots
某些模型有标识字段,这些字段一般是映射表的主键。Phalcon\Mvc\Model
可以识别标识字段,并在生成INSERT语句时忽略它,因此数据库可以自动为它生成一个值。建立记录以后,标识字段的值会被注册为数据库为其生成的值:
<?php $robot->save(); echo 'The generated id is: ', $robot->id;
Phalcon\Mvc\Model
可以识别标识字段,根据数据库系统,这些字段多是PostgreSQL的串行列,或者是MySQL的自增列。
PostgreSQL使用序列生成自增值,默认状况下,Phalcon试图从序列table_field_seq
中获取生成的值,例如:robots_id_seq
,若是序列具备其余名称,则须要实现getSequenceName()
方法:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function getSequenceName() { return 'robots_sequence_name'; } }
为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->skipAttributes( [ 'created_at', ] ); // UPDATE操做忽略字段 $this->skipAttributes( [ 'modified_in', ] ); } }
这将全局忽略应用程序中每一个INSERT / UPDATE操做的这些字段。若是想在不一样的INSERT / UPDATE操做时忽略不一样字段,能够传递第二个参数(布尔值) - true。强制使用默认值,实现方式以下:
<?php use Phalcon\Db\RawValue; use Store\Toys\Robots; $robot = new Robots(); $robot->name = 'Bender'; $robot->year = 1999; $robot->created_at = new RawValue('default'); $robot->create();
回调函数也能够用于为默认值建立分配条件:
<?php namespace Store\Toys; use Phalcon\Db\RawValue; use Phalcon\Mvc\Model; class Robots extends Model { public function beforeCreate() { if ($this->price > 10000) { $this->type = new RawValue('default'); } } }
切勿使用Phalcon\Db\RawValue
传递外部数据(如用户输入)或可变数据,由于参数绑定时,这些字段的值也会被忽略,因此有可能会被用来实施注入攻击。
UPDATE语句默认使用模型中定义的全部字段建立(全字段更新SQL),能够更改特定模型以进行动态更新,用须要更新的字段建立SQL语句。
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->useDynamicUpdate(true); } }
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', ]; } }
而后,可使用新的字段名:
<?php use Store\Toys\Robots; // 根据name查找一个记录 $robot = Robots::findFirst( 'theName = "Voltron"' ); echo $robot->theName, "\n"; // 根据type排序 $robot = Robots::find( [ 'order' => 'theType DESC', ] ); foreach ($robots as $robot) { echo 'Code: ', $robot->code, "\n"; } // 添加记录 $robot = new Robots(); $robot->code = '10101'; $robot->theName = 'Bender'; $robot->theType = 'Industrial'; $robot->theYear = 2999; $robot->save();
重命名字段时须要注意如下事项:
独立列映射容许:
查询时能够设置特定的模型以保持记录快照。可使用此功能来实现审计,或是根据持久性查询的数据了解哪些字段发生了改变:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->keepSnapshots(true); } }
当激活此功能时,应用程序会消耗更多的内存来与持久性查询的原始数据保持同步。在激活此功能的模型中,能够按以下方式检查发生改变的字段:
<?php use Store\Toys\Robots; // 查找一条记录 $robot = Robots::findFirst(); // 改变列值 $robot->name = 'Other name'; var_dump($robot->getChangedFields()); // ['name'] var_dump($robot->hasChanged('name')); // true var_dump($robot->hasChanged('type')); // false
快照会在模型建立 / 更新时自动更新,使用hasUpdated()
方法和getUploadedFields()
方法检查create / save / update操做后,字段是否更新。可是在afterUpdate()
,afterSave()
和afterCreate()
方法中调用getChangedFields()
方法,会致使应用程序出问题。
能够禁用此功能:
<?php Phalcon\Mvc\Model::setup( [ 'updateSnapshotOnSave' => false, ] );
也能够在php.ini
中设置:
phalcon.orm.update_snapshot_on_save = 0
使用此功能会产生如下效果:
<?php use Phalcon\Mvc\Model; class User extends Model { public function initialize() { $this->keepSnapshots(true); } } $user = new User(); $user->name = 'Test User'; $user->create(); var_dump($user->getChangedFields()); $user->login = 'testuser'; var_dump($user->getChangedFields()); $user->update(); var_dump($user->getChangedFields());
在Phalcon 3.1.0及以后的版本中:
array(0) { } array(1) { [0] => string(5) "login" } array(0) { }
getUpdatedFields()
方法将正确返回更新的字段,或如上所述,能够经过设置相关的ini值回到先前的行为。
若是模型映射到非默认的模式 / 数据库,可使用setSchema()
方法从新定义它:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->setSchema('toys'); } }
Phalcon应用中,全部模型能够属于相同的数据库链接,或具备独立的数据库链接。实际上,当Phalcon\Mvc\Model
须要链接数据库时,它会在应用程序的服务容器中请求数据库服务。能够在initialize()
方法中设置,覆盖该服务:
<?php use Phalcon\Db\Adapter\Pdo\Mysql as MysqlPdo; use Phalcon\Db\Adapter\Pdo\PostgreSQL as PostgreSQLPdo; // 注册MySQL数据库服务 $di->set( 'dbMysql', function () { return new MysqlPdo( [ 'host' => 'localhost', 'username' => 'root', 'password' => 'secret', 'dbname' => 'invo', ] ); } ); // 注册PostgreSQL数据库服务 $di->set( 'dbPostgres', function () { return new PostgreSQLPdo( [ 'host' => 'localhost', 'username' => 'postgres', 'password' => '', 'dbname' => 'invo', ] ); } );
而后,在initialize()
方法中为模型定义链接服务:
<?php 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 { /** * 动态选择分片 * @param array $intermediate * @param array $bindParams * @param array $bindTypes */ 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'); } } } // 使用默认分片 return $this->getDI()->get('dbShard0'); } }
selectReadConnection()
方法选择合适的链接,会影响任何新执行的查询:
<?php use Store\Toys\Robots; $robot = Robots::findFirst('id = 101');
有时可能须要在模型中访问应用程序服务,如下示例介绍了如何执行此操做:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public function notSaved() { // 从DI容器中获取flash服务 $flash = $this->getDI()->getFlash(); $messages = $this->getMessages(); // 显示验证消息 foreach ($messages as $message) { $flash->error($message); } } }
建立或更新操做执行失败时会触发notSaved
事件,所以咱们从DI容器中获取flash
服务,而后闪存验证消息。这样,咱们没必要在每次保存后打印消息。
在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 | 启用 / 禁用PhalconMvcModel::cloneResultMap() 方法的延迟绑定 |
false |
notNullValidations | ORM自动验证映射表中的非空列 | true |
parserCache | null |
|
phqlLiterals | 启用 / 禁用PHQL解析器中的字面量 | true |
uniqueCacheId | 3 |
|
updateSnapshotOnSave | 启用 / 禁用save() 方法的更新快照 |
true |
virtualForeignKeys | 启用 / 禁用虚拟外键 | true |
注意,给Phalcon\Mvc\Model::assign()
方法(建立 / 更新 / 保存模型中经常使用到它)传递参数时,setters
始终会被调用,这会给应用程序增长额外开销。能够在php.ini文件中添加配置phalcon.orm.disable_assign_setters = 1
,这样就只会简单的使用$this->property = value
。
下面演示以独立组件模式使用Phalcon\Mvc\Model
:
<?php use Phalcon\Di; use Phalcon\Mvc\Db\Adapter\Pdo\Sqlite as Connection; use Phalcon\Mvc\Model; use Phalcon\Mvc\Model\Manager as ModelsManager; 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();