模型表示應用程序信息(數據)以及這些數據的處理規則,主要用於管理與對應數據表的交互規則。大多數狀況下,數據庫中的每一張表都有對應的模型。應用程序中的大部分業務邏輯集中在模型中。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();