Phalcon7 模型的使用
模型代表了应用程序中的信息(数据)和处理数据的规则。模型主要用于管理与相应数据库表进行交互的规则。 大多数情况中,在应用程序中,数据库中每个表将对应一个模型。 应用程序中的大部分业务逻辑都将集中在模型里。
Phalcon\Mvc\Model 是 Phalcon 应用程序中所有模型的基类。它保证了数据库的独立性,基本的 CURD 操作, 高级的查询功能,多表关联等功能。Phalcon\Mvc\Model 不需要直接使用 SQL 语句,因为它的转换方法,会动态的调用相应的数据库引擎进行处理。
模型是数据库的高级抽象层。如果您想进行低层次的数据库操作,您可以查看 Phalcon\Db 组件文档。
创建模型
模型是一个继承自 Phalcon\Mvc\Model 的一个类。 它必须放到 models 文件夹。一个模型文件必须包含一个类, 同时它的类名必须符合驼峰命名法:
<?php use Phalcon\Mvc\Model; class Robots extends Model { }
上面的例子显示了 “Robots” 模型的实现。 需要注意的是 Robots 继承自 Phalcon\Mvc\Model 。 因此,Robots 模型拥有了大量继承自该组件功能,包括基本的数据库 CRUD (Create, Read, Update, Delete) 操作,数据验证以及复杂的搜索支持,并且可以同时关联多个模型。
默认情况下,模型 “Robots” 对应的是数据库表 “robots”, 如果想映射到其他数据库表,可以使用 getSource()
方法:
<?php use Phalcon\Mvc\Model; class Robots extends Model { public function getSource() { return "the_robots"; } }
模型 Robots 现在映射到了 “the_robots” 表。initialize()
方法可以帮助在模型中建立自定义行为,例如指定不同的数据库表。 initialize()
方法在请求期间只被调用一次。
<?php use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->setSource("the_robots"); } }
initialize()
方法在请求期间仅会被调用一次,目的是为应用中所有该模型的实例进行初始化。如果需要为每一个实例在创建的时候单独进行初始化, 可以使用 ‘onConstruct’ 事件:
<?php use Phalcon\Mvc\Model; class Robots extends Model { public function onConstruct() { // ... } }
公共属性对比设置与取值 Setters/Getters
模型可以通过公共属性的方式实现,意味着模型的所有属性在实例化该模型的地方可以无限制的读取和更新。
<?php use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public $price; }
通过使用 getters/setters 方法,可以控制哪些属性可以公开访问,并且对属性值执行不同的形式的转换,同时可以保存在模型中的数据添加相应的验证规则。
<?php use Phalcon\Mvc\Model; class Robots extends Model { protected $id; protected $name; protected $price; public function getId() { return $this->id; } public function setName($name) { // The name is too short? 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) { // Negative prices aren't allowed if ($price < 0) { throw new \InvalidArgumentException('Price can\'t be negative'); } $this->price = $price; } public function getPrice() { // Convert the value to double before be used return (double) $this->price; } }
公共属性的方式可以在开发中降低复杂度。而 getters/setters 的实现方式可以显著的增强应用的可测试性、扩展性和可维护性。 开发人员可以自己决定哪一种策略更加适合自己开发的应用。ORM同时兼容这两种方法。
模型放入命名空间
命名空间可以用来避免类名的冲突。ORM通过类名来映射相应的表名。比如 ‘Robots’:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { // ... }
Namespaces make part of model names when they are within strings:
<?php namespace Store\Toys; use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public function initialize() { $this->hasMany('id', 'Store\Toys\RobotsParts', 'robots_id'); } }
理解记录对象
每个模型的实例对应一条数据表中的记录。可以方便的通过读取对象的属性来访问相应的数据。比如, 一个表 “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 // Find record with id = 3 $robot = Robots::findFirst(3); // Prints "Terminator" echo $robot->name;
一旦记录被加载到内存中之后,你可以修改它的数据并保存所做的修改:
<?php $robot = Robots::findFirst(3); $robot->name = "RoboCop"; $robot->save();
如上所示,不需要写任何SQL语句。Phalcon\Mvc\Model 为web应用提供了高层数据库抽象。
查找记录
Phalcon\Mvc\Model 为数据查询提供了多种方法。下面的例子将演示如何从一个模型中查找一条或者多条记录:
<?php // How many robots are there? $robots = Robots::find(); echo "There are ", count($robots), "\n"; // How many mechanical robots are there? $robots = Robots::find("type = 'mechanical'"); echo "There are ", count($robots), "\n"; // Get and print virtual robots ordered by name $robots = Robots::find( array( "type = 'virtual'", "order" => "name" ) ); foreach ($robots as $robot) { echo $robot->name, "\n"; } // Get first 100 virtual robots ordered by name $robots = Robots::find( array( "type = 'virtual'", "order" => "name", "limit" => 100 ) ); foreach ($robots as $robot) { echo $robot->name, "\n"; }
如果需要通过外部数据(比如用户输入)或变量来查询记录,则必须要用`Binding Parameters`(绑定参数)的方式来防止SQL注入.
你可以使用 findFirst()
方法获取第一条符合查询条件的结果:
<?php // What's the first robot in robots table? $robot = Robots::findFirst(); echo "The robot name is ", $robot->name, "\n"; // What's the first mechanical robot in robots table? $robot = Robots::findFirst("type = 'mechanical'"); echo "The first mechanical robot name is ", $robot->name, "\n"; // Get first virtual robot ordered by name $robot = Robots::findFirst( array( "type = 'virtual'", "order" => "name" ) ); echo "The first virtual robot name is ", $robot->name, "\n";
find()
和 findFirst()
方法都接受关联数组作为查询条件:
<?php $robot = Robots::findFirst( array( "type = 'virtual'", "order" => "name DESC", "limit" => 30 ) ); $robots = Robots::find( array( "conditions" => "type = ?1", "bind" => array(1 => "virtual") ) );
可用的查询选项如下:
参数 | 描述 | 举例 |
---|---|---|
conditions | 查询操作的搜索条件。用于提取只有那些满足指定条件的记录。默认情况下Phalcon\Mvc\Model 假定第一个参数就是查询条件。 | "conditions" => "name LIKE'steve%'" |
columns | 只返回指定的字段,而不是模型所有的字段。 当用这个选项时,返回的是一个不完整的对象。 | "columns" => "id, name" |
bind | 绑定与选项一起使用,通过替换占位符以及转义字段值从而增加安全性。 | "bind" => array("status" =>"A", "type" => "some-time") |
bindTypes | 当绑定参数时,可以使用这个参数为绑定参数定义额外的类型限制从而更加增强安全性。 | "bindTypes" =>array(Column::BIND_PARAM_STR,Column::BIND_PARAM_INT) |
order | 用于结果排序。使用一个或者多个字段,逗号分隔。 | "order" => "name DESC,status" |
limit | 限制查询结果的数量在一定范围内。 | "limit" => 10 |
offset | Offset the results of the query by a certain amount | "offset" => 5 |
group | 从多条记录中获取数据并且根据一个或多个字段对结果进行分组。 | "group" => "name, status" |
for_update | 通过这个选项, Phalcon\Mvc\Model 读取最新的可用数据,并且为读到的每条记录设置独占锁。 | "for_update" => true |
shared_lock | 通过这个选项, Phalcon\Mvc\Model 读取最新的可用数据,并且为读到的每条记录设置共享锁。 | "shared_lock" => true |
cache | 缓存结果集,减少了连续访问数据库。 | "cache" => array("lifetime"=> 3600, "key" => "my-find-key") |
hydration | Sets the hydration strategy to represent each returned record in the result | "hydration" =>Resultset::HYDRATE_OBJECTS |
如果你愿意,除了使用数组作为查询参数外,还可以通过一种面向对象的方式来创建查询:
<?php $robots = Robots::query() ->where("type = :type:") ->andWhere("year < 2000") ->bind(array("type" => "mechanical")) ->order("name") ->execute();
静态方法 query()
返回一个对IDE自动完成友好的 Phalcon\Mvc\Model\Criteria 对象。
所有查询在内部都以 PHQL 查询的方式处理。PHQL是一个高层的、面向对象的类SQL语言。通过PHQL语言你可以使用更多的比如join其他模型、定义分组、添加聚集等特性。
最后,还有一个 findFirstBy<property-name>()
方法。这个方法扩展了前面提及的 findFirst()
方法。它允许您利用方法名中的属性名称,通过将要搜索的该字段的内容作为参数传给它,来快速从一个表执行检索操作。
还是用上面用过的 Robots 模型来举例说明:
<?php use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public $price; }
我们这里有3个属性:$id
, $name
和 $price
。因此,我们以想要查询第一个名称为 ‘Terminator’ 的记录为例,可以这样写:
<?php $name = "Terminator"; $robot = Robots::findFirstByName($name); if ($robot) { $this->flash->success("The first robot with the name " . $name . " cost " . $robot->price "."); } else { $this->flash->error("There were no robots found in our table with the name " . $name "."); }
请注意我们在方法调用中用的是 ‘Name’,并向它传递了变量 $name
, $name
的值就是我们想要找的记录的名称。另外注意,当我们的查询找到了符合的记录后,这个记录的其他属性也都是可用的。
模型结果集
findFirst()
方法直接返回一个被调用对象的实例(如果有结果返回的话),而 find()
方法返回一个 Phalcon\Mvc\Model\Resultset\Simple 对象。这个对象也封装进了所有结果集的功能,比如遍历、查找特定的记录、统计等等。
这些对象比一般数组功能更强大。最大的特点是 Phalcon\Mvc\Model\Resultset 每时每刻只有一个结果在内存中。这对操作大数据量时的内存管理相当有帮助。
<?php // Get all robots $robots = Robots::find(); // Traversing with a foreach foreach ($robots as $robot) { echo $robot->name, "\n"; } // Traversing with a while $robots->rewind(); while ($robots->valid()) { $robot = $robots->current(); echo $robot->name, "\n"; $robots->next(); } // Count the resultset echo count($robots); // Alternative way to count the resultset echo $robots->count(); // Move the internal cursor to the third robot $robots->seek(2); $robot = $robots->current(); // Access a robot by its position in the resultset $robot = $robots[5]; // Check if there is a record in certain position if (isset($robots[3])) { $robot = $robots[3]; } // Get the first record in the resultset $robot = $robots->getFirst(); // Get the last record $robot = $robots->getLast();
Phalcon 的结果集模拟了可滚动的游标,你可以通过位置,或者内部指针去访问任何一条特定的记录。注意有一些数据库系统不支持滚动游标,这就使得查询会被重复执行, 以便回放光标到最开始的位置,然后获得相应的记录。类似地,如果多次遍历结果集,那么必须执行相同的查询次数。
将大数据量的查询结果存储在内存会消耗很多资源,正因为如此,分成每32行一块从数据库中获得结果集,以减少重复执行查询请求的次数,在一些情况下也节省内存。
注意结果集可以序列化后保存在一个后端缓存里面。 Phalcon\Cache 可以用来实现这个。但是,序列化数据会导致 Phalcon\Mvc\Model 将从数据库检索到的所有数据以一个数组的方式保存,因此在这样执行的地方会消耗更多的内存。
<?php // Query all records from model parts $parts = Parts::find(); // Store the resultset into a file file_put_contents("cache.txt", serialize($parts)); // Get parts from file $parts = unserialize(file_get_contents("cache.txt")); // Traverse the parts foreach ($parts as $part) { echo $part->id; }
过滤结果集(Filtering Resultsets)¶
过滤数据最有效的方法是设置一些查询条件,数据库会利用表的索引快速返回数据。Phalcon 额外的允许你通过任何数据库不支持的方式过滤数据。
<?php $customers = Customers::find()->filter( function ($customer) { // Return only customers with a valid e-mail if (filter_var($customer->email, FILTER_VALIDATE_EMAIL)) { return $customer; } } );
将结果集转为数组
<?php $customers = Customers::find(); $arr = $customers->toArray();
绑定参数
在 Phalcon\Mvc\Model 中也支持绑定参数。即使使用绑定参数对性能有一点很小的影响,还是强烈建议您使用这种方法,以消除代码受SQL注入攻击的可能性。 绑定参数支持字符串和整数占位符。实现方法如下:
<?php // Query robots binding parameters with string placeholders $conditions = "name = :name: AND type = :type:"; // Parameters whose keys are the same as placeholders $parameters = array( "name" => "Robotina", "type" => "maid" ); // Perform the query $robots = Robots::find( array( $conditions, "bind" => $parameters ) ); // Query robots binding parameters with integer placeholders $conditions = "name = ?1 AND type = ?2"; $parameters = array(1 => "Robotina", 2 => "maid"); $robots = Robots::find( array( $conditions, "bind" => $parameters ) ); // Query robots binding parameters with both string and integer placeholders $conditions = "name = :name: AND type = ?1"; // Parameters whose keys are the same as placeholders $parameters = array( "name" => "Robotina", 1 => "maid" ); // Perform the query $robots = Robots::find( array( $conditions, "bind" => $parameters ) );