模型代表了應(yīng)用程序中的信息(數(shù)據(jù))和處理數(shù)據(jù)的規(guī)則。模型主要用于管理與相應(yīng)數(shù)據(jù)庫(kù)表進(jìn)行交互的規(guī)則。 大多數(shù)情況中,在應(yīng)用程序中,數(shù)據(jù)庫(kù)中每個(gè)表將對(duì)應(yīng)一個(gè)模型。 應(yīng)用程序中的大部分業(yè)務(wù)邏輯都將集中在模型里。
Phalcon\Mvc\Model 是 Phalcon 應(yīng)用程序中所有模型的基類。它保證了數(shù)據(jù)庫(kù)的獨(dú)立性,基本的 CURD 操作, 高級(jí)的查詢功能,多表關(guān)聯(lián)等功能。Phalcon\Mvc\Model 不需要直接使用 SQL 語句,因?yàn)樗霓D(zhuǎn)換方法,會(huì)動(dòng)態(tài)的調(diào)用相應(yīng)的數(shù)據(jù)庫(kù)引擎進(jìn)行處理。
模型是數(shù)據(jù)庫(kù)的高級(jí)抽象層。如果您想進(jìn)行低層次的數(shù)據(jù)庫(kù)操作,您可以查看 Phalcon\Db 組件文檔。
模型是一個(gè)繼承自 Phalcon\Mvc\Model 的一個(gè)類。 它必須放到 models 文件夾。一個(gè)模型文件必須包含一個(gè)類, 同時(shí)它的類名必須符合駝峰命名法:
<?php use Phalcon\Mvc\Model; class Robots extends Model { }
上面的例子顯示了 “Robots” 模型的實(shí)現(xiàn)。 需要注意的是 Robots 繼承自 Phalcon\Mvc\Model 。 因此,Robots 模型擁有了大量繼承自該組件功能,包括基本的數(shù)據(jù)庫(kù) CRUD (Create, Read, Update, Delete) 操作,數(shù)據(jù)驗(yàn)證以及復(fù)雜的搜索支持,并且可以同時(shí)關(guān)聯(lián)多個(gè)模型。
默認(rèn)情況下,模型 “Robots” 對(duì)應(yīng)的是數(shù)據(jù)庫(kù)表 “robots”, 如果想映射到其他數(shù)據(jù)庫(kù)表,可以使用 getSource()
方法:
<?php use Phalcon\Mvc\Model; class Robots extends Model { public function getSource() { return "the_robots"; } }
模型 Robots 現(xiàn)在映射到了 “the_robots” 表。initialize()
方法可以幫助在模型中建立自定義行為,例如指定不同的數(shù)據(jù)庫(kù)表。 initialize()
方法在請(qǐng)求期間只被調(diào)用一次。
<?php use Phalcon\Mvc\Model; class Robots extends Model { public function initialize() { $this->setSource("the_robots"); } }
initialize()
方法在請(qǐng)求期間僅會(huì)被調(diào)用一次,目的是為應(yīng)用中所有該模型的實(shí)例進(jìn)行初始化。如果需要為每一個(gè)實(shí)例在創(chuàng)建的時(shí)候單獨(dú)進(jìn)行初始化, 可以使用 ‘onConstruct’ 事件:
<?php use Phalcon\Mvc\Model; class Robots extends Model { public function onConstruct() { // ... } }
模型可以通過公共屬性的方式實(shí)現(xiàn),意味著模型的所有屬性在實(shí)例化該模型的地方可以無限制的讀取和更新。
<?php use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public $price; }
通過使用 getters/setters 方法,可以控制哪些屬性可以公開訪問,并且對(duì)屬性值執(zhí)行不同的形式的轉(zhuǎn)換,同時(shí)可以保存在模型中的數(shù)據(jù)添加相應(yīng)的驗(yàn)證規(guī)則。
<?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; } }
公共屬性的方式可以在開發(fā)中降低復(fù)雜度。而 getters/setters 的實(shí)現(xiàn)方式可以顯著的增強(qiáng)應(yīng)用的可測(cè)試性、擴(kuò)展性和可維護(hù)性。 開發(fā)人員可以自己決定哪一種策略更加適合自己開發(fā)的應(yīng)用。ORM同時(shí)兼容這兩種方法。
命名空間可以用來避免類名的沖突。ORM通過類名來映射相應(yīng)的表名。比如 ‘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'); } }
每個(gè)模型的實(shí)例對(duì)應(yīng)一條數(shù)據(jù)表中的記錄??梢苑奖愕耐ㄟ^讀取對(duì)象的屬性來訪問相應(yīng)的數(shù)據(jù)。比如, 一個(gè)表 “robots” 有如下數(shù)據(jù):
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;
一旦記錄被加載到內(nèi)存中之后,你可以修改它的數(shù)據(jù)并保存所做的修改:
<?php $robot = Robots::findFirst(3); $robot->name = "RoboCop"; $robot->save();
如上所示,不需要寫任何SQL語句。Phalcon\Mvc\Model 為web應(yīng)用提供了高層數(shù)據(jù)庫(kù)抽象。
Phalcon\Mvc\Model 為數(shù)據(jù)查詢提供了多種方法。下面的例子將演示如何從一個(gè)模型中查找一條或者多條記錄:
<?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"; }
如果需要通過外部數(shù)據(jù)(比如用戶輸入)或變量來查詢記錄,則必須要用`Binding Parameters`(綁定參數(shù))的方式來防止SQL注入.
你可以使用 findFirst()
方法獲取第一條符合查詢條件的結(jié)果:
<?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()
方法都接受關(guān)聯(lián)數(shù)組作為查詢條件:
<?php $robot = Robots::findFirst( array( "type = 'virtual'", "order" => "name DESC", "limit" => 30 ) ); $robots = Robots::find( array( "conditions" => "type = ?1", "bind" => array(1 => "virtual") ) );
可用的查詢選項(xiàng)如下:
參數(shù) | 描述 | 舉例 |
---|---|---|
conditions | 查詢操作的搜索條件。用于提取只有那些滿足指定條件的記錄。默認(rèn)情況下Phalcon\Mvc\Model 假定第一個(gè)參數(shù)就是查詢條件。 | "conditions" => "name LIKE'steve%'" |
columns | 只返回指定的字段,而不是模型所有的字段。 當(dāng)用這個(gè)選項(xiàng)時(shí),返回的是一個(gè)不完整的對(duì)象。 | "columns" => "id, name" |
bind | 綁定與選項(xiàng)一起使用,通過替換占位符以及轉(zhuǎn)義字段值從而增加安全性。 | "bind" => array("status" =>"A", "type" => "some-time") |
bindTypes | 當(dāng)綁定參數(shù)時(shí),可以使用這個(gè)參數(shù)為綁定參數(shù)定義額外的類型限制從而更加增強(qiáng)安全性。 | "bindTypes" =>array(Column::BIND_PARAM_STR,Column::BIND_PARAM_INT) |
order | 用于結(jié)果排序。使用一個(gè)或者多個(gè)字段,逗號(hào)分隔。 | "order" => "name DESC,status" |
limit | 限制查詢結(jié)果的數(shù)量在一定范圍內(nèi)。 | "limit" => 10 |
offset | Offset the results of the query by a certain amount | "offset" => 5 |
group | 從多條記錄中獲取數(shù)據(jù)并且根據(jù)一個(gè)或多個(gè)字段對(duì)結(jié)果進(jìn)行分組。 | "group" => "name, status" |
for_update | 通過這個(gè)選項(xiàng), Phalcon\Mvc\Model 讀取最新的可用數(shù)據(jù),并且為讀到的每條記錄設(shè)置獨(dú)占鎖。 | "for_update" => true |
shared_lock | 通過這個(gè)選項(xiàng), Phalcon\Mvc\Model 讀取最新的可用數(shù)據(jù),并且為讀到的每條記錄設(shè)置共享鎖。 | "shared_lock" => true |
cache | 緩存結(jié)果集,減少了連續(xù)訪問數(shù)據(jù)庫(kù)。 | "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 |
如果你愿意,除了使用數(shù)組作為查詢參數(shù)外,還可以通過一種面向?qū)ο蟮姆绞絹韯?chuàng)建查詢:
<?php $robots = Robots::query() ->where("type = :type:") ->andWhere("year < 2000") ->bind(array("type" => "mechanical")) ->order("name") ->execute();
靜態(tài)方法 query()
返回一個(gè)對(duì)IDE自動(dòng)完成友好的 Phalcon\Mvc\Model\Criteria 對(duì)象。
所有查詢?cè)趦?nèi)部都以 PHQL 查詢的方式處理。PHQL是一個(gè)高層的、面向?qū)ο蟮念怱QL語言。通過PHQL語言你可以使用更多的比如join其他模型、定義分組、添加聚集等特性。
最后,還有一個(gè) findFirstBy<property-name>()
方法。這個(gè)方法擴(kuò)展了前面提及的 findFirst()
方法。它允許您利用方法名中的屬性名稱,通過將要搜索的該字段的內(nèi)容作為參數(shù)傳給它,來快速?gòu)囊粋€(gè)表執(zhí)行檢索操作。
還是用上面用過的 Robots 模型來舉例說明:
<?php use Phalcon\Mvc\Model; class Robots extends Model { public $id; public $name; public $price; }
我們這里有3個(gè)屬性:$id
, $name
和 $price
。因此,我們以想要查詢第一個(gè)名稱為 ‘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 "."); }
請(qǐng)注意我們?cè)诜椒ㄕ{(diào)用中用的是 ‘Name’,并向它傳遞了變量 $name
, $name
的值就是我們想要找的記錄的名稱。另外注意,當(dāng)我們的查詢找到了符合的記錄后,這個(gè)記錄的其他屬性也都是可用的。
findFirst()
方法直接返回一個(gè)被調(diào)用對(duì)象的實(shí)例(如果有結(jié)果返回的話),而 find()
方法返回一個(gè) Phalcon\Mvc\Model\Resultset\Simple 對(duì)象。這個(gè)對(duì)象也封裝進(jìn)了所有結(jié)果集的功能,比如遍歷、查找特定的記錄、統(tǒng)計(jì)等等。
這些對(duì)象比一般數(shù)組功能更強(qiáng)大。最大的特點(diǎn)是 Phalcon\Mvc\Model\Resultset 每時(shí)每刻只有一個(gè)結(jié)果在內(nèi)存中。這對(duì)操作大數(shù)據(jù)量時(shí)的內(nèi)存管理相當(dāng)有幫助。
<?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 的結(jié)果集模擬了可滾動(dòng)的游標(biāo),你可以通過位置,或者內(nèi)部指針去訪問任何一條特定的記錄。注意有一些數(shù)據(jù)庫(kù)系統(tǒng)不支持滾動(dòng)游標(biāo),這就使得查詢會(huì)被重復(fù)執(zhí)行, 以便回放光標(biāo)到最開始的位置,然后獲得相應(yīng)的記錄。類似地,如果多次遍歷結(jié)果集,那么必須執(zhí)行相同的查詢次數(shù)。
將大數(shù)據(jù)量的查詢結(jié)果存儲(chǔ)在內(nèi)存會(huì)消耗很多資源,正因?yàn)槿绱?,分成?2行一塊從數(shù)據(jù)庫(kù)中獲得結(jié)果集,以減少重復(fù)執(zhí)行查詢請(qǐng)求的次數(shù),在一些情況下也節(jié)省內(nèi)存。
注意結(jié)果集可以序列化后保存在一個(gè)后端緩存里面。 Phalcon\Cache 可以用來實(shí)現(xiàn)這個(gè)。但是,序列化數(shù)據(jù)會(huì)導(dǎo)致 Phalcon\Mvc\Model 將從數(shù)據(jù)庫(kù)檢索到的所有數(shù)據(jù)以一個(gè)數(shù)組的方式保存,因此在這樣執(zhí)行的地方會(huì)消耗更多的內(nèi)存。
<?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; }
過濾數(shù)據(jù)最有效的方法是設(shè)置一些查詢條件,數(shù)據(jù)庫(kù)會(huì)利用表的索引快速返回?cái)?shù)據(jù)。Phalcon 額外的允許你通過任何數(shù)據(jù)庫(kù)不支持的方式過濾數(shù)據(jù)。
<?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 中也支持綁定參數(shù)。即使使用綁定參數(shù)對(duì)性能有一點(diǎn)很小的影響,還是強(qiáng)烈建議您使用這種方法,以消除代碼受SQL注入攻擊的可能性。 綁定參數(shù)支持字符串和整數(shù)占位符。實(shí)現(xiàn)方法如下:
<?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 ) );
更多建議: