# 使用模型(Working with Models)
# 使用模型(Working with Models)
模型代表了應用程序中的信息(數據)和處理數據的規則。模型主要用于管理與相應數據庫表進行交互的規則。大多數情況中,在應用程序中,數據庫中每個表將對應一個模型。應用程序中的大部分業務邏輯都將集中在模型里。
[*Phalcon\\Mvc\\Model*](#) 是 Phalcon 應用程序中所有模型的基類。它保證了數據庫的獨立性,基本的 CURD 操作,高級的查詢功能,多表關聯等功能。[*Phalcon\\Mvc\\Model*](#) 不需要直接使用 SQL 語句,因為它的轉換方法,會動態的調用相應的數據庫引擎進行處理。
> 模型是數據庫的高級抽象層。如果您想進行低層次的數據庫操作,您可以查看[*Phalcon\\Db*](#) 組件文檔。
### 創建模型
模型是一個繼承自 [*Phalcon\\Mvc\\Model*](#) 的一個類。 它必須放到 models 文件夾。一個模型文件必須包含一個類,同時它的類名必須符合駝峰命名法:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
}
```
```
上面的例子顯示了 “Robots” 模型的實現。 需要注意的是 Robots 繼承自 [*Phalcon\\Mvc\\Model*](#) 。因此,Robots 模型擁有了大量繼承自該組件功能,包括基本的數據庫 CRUD (Create, Read, Update, Delete) 操作,數據驗證以及復雜的搜索支持,并且可以同時關聯多個模型。
> 如果使用 PHP 5.4/5.5 建議在模型中預先定義好所有的列,這樣可以減少模型內存的開銷以及內存分配。
默認情況下,模型 “Robots” 對應的是數據庫表 “robots”, 如果想映射到其他數據庫表,可以使用 getSource() 方法:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function getSource()
{
return "the_robots";
}
}
```
```
模型 Robots 現在映射到了 “the\_robots” 表。initialize() 方法可以幫助在模型中建立自定義行為,例如指定不同的數據庫表。initialize() 方法在請求期間只被調用一次。
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->setSource("the_robots");
}
}
```
```
initialize() 方法在請求期間僅會被調用一次,目的是為應用中所有該模型的實例進行初始化。如果需要為每一個實例在創建的時候單獨進行初始化,可以使用 ‘onConstruct' 事件:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function onConstruct()
{
// ...
}
}
```
```
### 公共屬性對比設置與取值 Setters/Getters(Public properties vs. Setters/Getters)
模型可以通過公共屬性的方式實現,意味著模型的所有屬性在實例化該模型的地方可以無限制的讀取和更新。
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $id;
public $name;
public $price;
}
```
```
通過使用 getters/setters 方法,可以控制哪些屬性可以公開訪問,并且對屬性值執行不同的形式的轉換,同時可以保存在模型中的數據添加相應的驗證規則。
```
<pre class="calibre14">```
<?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同時兼容這兩種方法。
### 模型放入命名空間(Models in Namespaces)
命名空間可以用來避免類名的沖突。ORM通過類名來映射相應的表名。比如 ‘Robots':
```
<pre class="calibre14">```
<?php
namespace Store\Toys;
use Phalcon\Mvc\Model;
class Robots extends Model
{
// ...
}
```
```
Namespaces make part of model names when they are within strings:
```
<pre class="calibre14">```
<?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');
}
}
```
```
### 理解記錄對象(Understanding Records To Objects)
每個模型的實例對應一條數據表中的記錄。可以方便的通過讀取對象的屬性來訪問相應的數據。比如,一個表 “robots” 有如下數據:
```
<pre class="calibre14">```
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)
```
```
你可以通過主鍵找到某一條記錄并且打印它的名稱:
```
<pre class="calibre14">```
<?php
// Find record with id = 3
$robot = Robots::findFirst(3);
// Prints "Terminator"
echo $robot->name;
```
```
一旦記錄被加載到內存中之后,你可以修改它的數據并保存所做的修改:
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst(3);
$robot->name = "RoboCop";
$robot->save();
```
```
如上所示,不需要寫任何SQL語句。[*Phalcon\\Mvc\\Model*](#) 為web應用提供了高層數據庫抽象。
### 查找記錄(Finding Records)
[*Phalcon\\Mvc\\Model*](#) 為數據查詢提供了多種方法。下面的例子將演示如何從一個模型中查找一條或者多條記錄:
```
<pre class="calibre14">```
<?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";
}
```
```
> If you want find record by external data (such as user input) or variable data you must use [`Binding Parameters`\_](#).
你可以使用 findFirst() 方法獲取第一條符合查詢條件的結果:
```
<pre class="calibre14">```
<?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() 方法都接受關聯數組作為查詢條件:
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst(
array(
"type = 'virtual'",
"order" => "name DESC",
"limit" => 30
)
);
$robots = Robots::find(
array(
"conditions" => "type = ?1",
"bind" => array(1 => "virtual")
)
);
```
```
可用的查詢選項如下:
如果你愿意,除了使用數組作為查詢參數外,還可以通過一種面向對象的方式來創建查詢:
```
<pre class="calibre14">```
<?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() 方法。這個方法擴展了前面提及的 “findFirst()” 方法。它允許您利用方法名中的屬性名稱,通過將要搜索的該字段的內容作為參數傳給它,來快速從一個表執行檢索操作。
還是用上面用過的 Robots 模型來舉例說明:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $id;
public $name;
public $price;
}
```
```
我們這里有3個屬性:$id, $name 和 $price。因此,我們以想要查詢第一個名稱為 ‘Terminator' 的記錄為例,可以這樣寫:
```
<pre class="calibre14">```
<?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 的值就是我們想要找的記錄的名稱。另外注意,當我們的查詢找到了符合的記錄后,這個記錄的其他屬性也都是可用的。
### 模型結果集(Model Resultsets)
findFirst() 方法直接返回一個被調用對象的實例(如果有結果返回的話),而 find() 方法返回一個 [*Phalcon\\Mvc\\Model\\Resultset\\Simple*](#) 對象。這個對象也封裝進了所有結果集的功能,比如遍歷、查找特定的記錄、統計等等。
這些對象比一般數組功能更強大。最大的特點是 [*Phalcon\\Mvc\\Model\\Resultset*](#) 每時每刻只有一個結果在內存中。這對操作大數據量時的內存管理相當有幫助。
```
<pre class="calibre14">```
<?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*](#)將從數據庫檢索到的所有數據以一個數組的方式保存,因此在這樣執行的地方會消耗更多的內存。
```
<pre class="calibre14">```
<?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 額外的允許你通過任何數據庫不支持的方式過濾數據。
```
<pre class="calibre14">```
<?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;
}
}
);
```
```
### 綁定參數(Binding Parameters)
在 [*Phalcon\\Mvc\\Model*](#) 中也支持綁定參數。即使使用綁定參數對性能有一點很小的影響,還是強烈建議您使用這種方法,以消除代碼受SQL注入攻擊的可能性。綁定參數支持字符串和整數占位符。實現方法如下:
```
<pre class="calibre14">```
<?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
)
);
```
```
When using numeric placeholders, you will need to define them as integers i.e. 1 or 2. In this case “1” or “2” are considered stringsand not numbers, so the placeholder could not be successfully replaced.
Strings are automatically escaped using [PDO](http://www.php.net/manual/en/pdo.prepared-statements.php). This function takes into account the connection charset, so its recommended to definethe correct charset in the connection parameters or in the database configuration, as a wrong charset will produce undesired effectswhen storing or retrieving data.
Additionally you can set the parameter “bindTypes”, this allows defining how the parameters should be bound according to its data type:
```
<pre class="calibre14">```
<?php
use Phalcon\Db\Column;
// Bind parameters
$parameters = array(
"name" => "Robotina",
"year" => 2008
);
// Casting Types
$types = array(
"name" => Column::BIND_PARAM_STR,
"year" => Column::BIND_PARAM_INT
);
// Query robots binding parameters with string placeholders
$robots = Robots::find(
array(
"name = :name: AND year = :year:",
"bind" => $parameters,
"bindTypes" => $types
)
);
```
```
> Since the default bind-type is \\Phalcon\\Db\\Column::BIND\_PARAM\_STR, there is no need to specify the“bindTypes” parameter if all of the columns are of that type.
If you bind arrays in bound parameters, keep in mind, that keys must be numbered from zero:
```
<pre class="calibre14">```
<?php
$array = ["a","b","c"]; // $array: [[0] => "a", [1] => "b", [2] => "c"]
unset($array[1]); // $array: [[0] => "a", [2] => "c"]
// Now we have to renumber the keys
$array = array_values($array); // $array: [[0] => "a", [1] => "c"]
$robots = Robots::find(
array(
'letter IN ({letter:array})',
'bind' => array(
'letter' => $array
)
)
);
```
```
Bound parameters are available for all query methods such as find() and findFirst() but also the calculationmethods like count(), sum(), average() etc.
If you're using “finders”, bound parameters are automatically used for you:
```
<pre class="calibre14">```
<?php
// Explicit query using bound parameters
$robots = Robots::find(
array(
"name = ?0",
"bind" => ["Ultron"],
)
);
// Implicit query using bound parameters
$robots = Robots::findByName("Ultron");
```
```
### 獲取記錄的初始化以及準備(Initializing/Preparing fetched records)
May be the case that after obtaining a record from the database is necessary to initialise the data beforebeing used by the rest of the application. You can implement the method ‘afterFetch' in a model, this eventwill be executed just after create the instance and assign the data to it:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $id;
public $name;
public $status;
public function beforeSave()
{
// Convert the array into a string
$this->status = join(',', $this->status);
}
public function afterFetch()
{
// Convert the string to an array
$this->status = explode(',', $this->status);
}
}
```
```
If you use getters/setters instead of/or together with public properties, you can initialize the field once it isaccessed:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $id;
public $name;
public $status;
public function getStatus()
{
return explode(',', $this->status);
}
}
```
```
### 模型關系(Relationships between Models)
There are four types of relationships: one-on-one, one-to-many, many-to-one and many-to-many. The relationship may beunidirectional or bidirectional, and each can be simple (a one to one model) or more complex (a combination of models).The model manager manages foreign key constraints for these relationships, the definition of these helps referentialintegrity as well as easy and fast access of related records to a model. Through the implementation of relations,it is easy to access data in related models from each record in a uniform way.
有四種關系類型:1對1,1對多,多對1,多對多。關系可以是單向或者雙向的,每個關系可以是簡單的(一個1對1的模型)也可以是復雜的(1組模型)。
### 單向關系(Unidirectional relationships)
Unidirectional relations are those that are generated in relation to one another but not vice versa.
### 雙向關系(Bidirectional relations)
The bidirectional relations build relationships in both models and each model defines the inverse relationship of the other.
### 定義關系(Defining relationships)
In Phalcon, relationships must be defined in the initialize() method of a model. The methods belongsTo(), hasOne(),hasMany() and hasManyToMany() define the relationship between one or more fields from the current model to fields inanother model. Each of these methods requires 3 parameters: local fields, referenced model, referenced fields.
MethodDescriptionhasManyDefines a 1-n relationshiphasOneDefines a 1-1 relationshipbelongsToDefines a n-1 relationshiphasManyToManyDefines a n-n relationshipThe following schema shows 3 tables whose relations will serve us as an example regarding relationships:
```
<pre class="calibre14">```
CREATE TABLE `robots` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(70) NOT NULL,
`type` varchar(32) NOT NULL,
`year` int(11) NOT NULL,
PRIMARY KEY (`id`)
);
CREATE TABLE `robots_parts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`robots_id` int(10) NOT NULL,
`parts_id` int(10) NOT NULL,
`created_at` DATE NOT NULL,
PRIMARY KEY (`id`),
KEY `robots_id` (`robots_id`),
KEY `parts_id` (`parts_id`)
);
CREATE TABLE `parts` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(70) NOT NULL,
PRIMARY KEY (`id`)
);
```
```
- The model “Robots” has many “RobotsParts”.
- The model “Parts” has many “RobotsParts”.
- The model “RobotsParts” belongs to both “Robots” and “Parts” models as a many-to-one relation.
- The model “Robots” has a relation many-to-many to “Parts” through “RobotsParts”.
Check the EER diagram to understand better the relations:

The models with their relations could be implemented as follows:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $id;
public $name;
public function initialize()
{
$this->hasMany("id", "RobotsParts", "robots_id");
}
}
```
```
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Parts extends Model
{
public $id;
public $name;
public function initialize()
{
$this->hasMany("id", "RobotsParts", "parts_id");
}
}
```
```
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class RobotsParts extends Model
{
public $id;
public $robots_id;
public $parts_id;
public function initialize()
{
$this->belongsTo("robots_id", "Robots", "id");
$this->belongsTo("parts_id", "Parts", "id");
}
}
```
```
The first parameter indicates the field of the local model used in the relationship; the second indicates the nameof the referenced model and the third the field name in the referenced model. You could also use arrays to define multiple fields in the relationship.
Many to many relationships require 3 models and define the attributes involved in the relationship:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $id;
public $name;
public function initialize()
{
$this->hasManyToMany(
"id",
"RobotsParts",
"robots_id", "parts_id",
"Parts",
"id"
);
}
}
```
```
### 使用關系(Taking advantage of relationships)
When explicitly defining the relationships between models, it is easy to find related records for a particular record.
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst(2);
foreach ($robot->robotsParts as $robotPart) {
echo $robotPart->parts->name, "\n";
}
```
```
Phalcon uses the magic methods \_\_set/\_\_get/\_\_call to store or retrieve related data using relationships.
By accessing an attribute with the same name as the relationship will retrieve all its related record(s).
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst();
$robotsParts = $robot->robotsParts; // All the related records in RobotsParts
```
```
Also, you can use a magic getter:
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst();
$robotsParts = $robot->getRobotsParts(); // All the related records in RobotsParts
$robotsParts = $robot->getRobotsParts(array('limit' => 5)); // Passing parameters
```
```
If the called method has a “get” prefix [*Phalcon\\Mvc\\Model*](#) will return afindFirst()/find() result. The following example compares retrieving related results with using magic methodsand without:
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst(2);
// Robots model has a 1-n (hasMany)
// relationship to RobotsParts then
$robotsParts = $robot->robotsParts;
// Only parts that match conditions
$robotsParts = $robot->getRobotsParts("created_at = '2015-03-15'");
// Or using bound parameters
$robotsParts = $robot->getRobotsParts(
array(
"created_at = :date:",
"bind" => array(
"date" => "2015-03-15"
)
)
);
$robotPart = RobotsParts::findFirst(1);
// RobotsParts model has a n-1 (belongsTo)
// relationship to RobotsParts then
$robot = $robotPart->robots;
```
```
Getting related records manually:
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst(2);
// Robots model has a 1-n (hasMany)
// relationship to RobotsParts, then
$robotsParts = RobotsParts::find("robots_id = '" . $robot->id . "'");
// Only parts that match conditions
$robotsParts = RobotsParts::find(
"robots_id = '" . $robot->id . "' AND created_at = '2015-03-15'"
);
$robotPart = RobotsParts::findFirst(1);
// RobotsParts model has a n-1 (belongsTo)
// relationship to RobotsParts then
$robot = Robots::findFirst("id = '" . $robotPart->robots_id . "'");
```
```
The prefix “get” is used to find()/findFirst() related records. Depending on the type of relation it will use‘find' or ‘findFirst':
TypeDescriptionImplicit MethodBelongs-ToReturns a model instance of the related record directlyfindFirstHas-OneReturns a model instance of the related record directlyfindFirstHas-ManyReturns a collection of model instances of the referenced modelfindHas-Many-to-ManyReturns a collection of model instances of the referenced model, it implicitly does ‘inner joins' with the involved models(complex query)You can also use “count” prefix to return an integer denoting the count of the related records:
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst(2);
echo "The robot has ", $robot->countRobotsParts(), " parts\n";
```
```
### 定義關系(Aliasing Relationships)
To explain better how aliases work, let's check the following example:
The “robots\_similar” table has the function to define what robots are similar to others:
```
<pre class="calibre14">```
mysql> desc robots_similar;
+-------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| robots_id | int(10) unsigned | NO | MUL | NULL | |
| similar_robots_id | int(10) unsigned | NO | | NULL | |
+-------------------+------------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)
```
```
Both “robots\_id” and “similar\_robots\_id” have a relation to the model Robots:

A model that maps this table and its relationships is the following:
```
<pre class="calibre14">```
<?php
class RobotsSimilar extends Phalcon\Mvc\Model
{
public function initialize()
{
$this->belongsTo('robots_id', 'Robots', 'id');
$this->belongsTo('similar_robots_id', 'Robots', 'id');
}
}
```
```
Since both relations point to the same model (Robots), obtain the records related to the relationship could not be clear:
```
<pre class="calibre14">```
<?php
$robotsSimilar = RobotsSimilar::findFirst();
// Returns the related record based on the column (robots_id)
// Also as is a belongsTo it's only returning one record
// but the name 'getRobots' seems to imply that return more than one
$robot = $robotsSimilar->getRobots();
// but, how to get the related record based on the column (similar_robots_id)
// if both relationships have the same name?
```
```
The aliases allow us to rename both relationships to solve these problems:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class RobotsSimilar extends Model
{
public function initialize()
{
$this->belongsTo(
'robots_id',
'Robots',
'id',
array(
'alias' => 'Robot'
)
);
$this->belongsTo(
'similar_robots_id',
'Robots',
'id',
array(
'alias' => 'SimilarRobot'
)
);
}
}
```
```
With the aliasing we can get the related records easily:
```
<pre class="calibre14">```
<?php
$robotsSimilar = RobotsSimilar::findFirst();
// Returns the related record based on the column (robots_id)
$robot = $robotsSimilar->getRobot();
$robot = $robotsSimilar->robot;
// Returns the related record based on the column (similar_robots_id)
$similarRobot = $robotsSimilar->getSimilarRobot();
$similarRobot = $robotsSimilar->similarRobot;
```
```
### 魔術方法 Getters 對比顯示方法(Magic Getters vs. Explicit methods)
Most IDEs and editors with auto-completion capabilities can not infer the correct types when using magic getters,instead of use the magic getters you can optionally define those methods explicitly with the correspondingdocblocks helping the IDE to produce a better auto-completion:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $id;
public $name;
public function initialize()
{
$this->hasMany("id", "RobotsParts", "robots_id");
}
/**
* Return the related "robots parts"
*
* @return \RobotsParts[]
*/
public function getRobotsParts($parameters = null)
{
return $this->getRelated('RobotsParts', $parameters);
}
}
```
```
### 虛擬外鍵(Virtual Foreign Keys)
By default, relationships do not act like database foreign keys, that is, if you try to insert/update a value without having a validvalue in the referenced model, Phalcon will not produce a validation message. You can modify this behavior by adding a fourth parameterwhen defining a relationship.
The RobotsPart model can be changed to demonstrate this feature:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class RobotsParts extends Model
{
public $id;
public $robots_id;
public $parts_id;
public function initialize()
{
$this->belongsTo(
"robots_id",
"Robots",
"id",
array(
"foreignKey" => true
)
);
$this->belongsTo(
"parts_id",
"Parts",
"id",
array(
"foreignKey" => array(
"message" => "The part_id does not exist on the Parts model"
)
)
);
}
}
```
```
If you alter a belongsTo() relationship to act as foreign key, it will validate that the values inserted/updated on those fields have avalid value on the referenced model. Similarly, if a hasMany()/hasOne() is altered it will validate that the records cannot be deletedif that record is used on a referenced model.
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Parts extends Model
{
public function initialize()
{
$this->hasMany(
"id",
"RobotsParts",
"parts_id",
array(
"foreignKey" => array(
"message" => "The part cannot be deleted because other robots are using it"
)
)
);
}
}
```
```
A virtual foreign key can be set up to allow null values as follows:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class RobotsParts extends Model
{
public $id;
public $robots_id;
public $parts_id;
public function initialize()
{
$this->belongsTo(
"parts_id",
"Parts",
"id",
array(
"foreignKey" => array(
"allowNulls" => true,
"message" => "The part_id does not exist on the Parts model"
)
)
);
}
}
```
```
### 級聯與限制動作(Cascade/Restrict actions)
Relationships that act as virtual foreign keys by default restrict the creation/update/deletion of recordsto maintain the integrity of data:
```
<pre class="calibre14">```
<?php
namespace Store\Models;
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Relation;
class Robots extends Model
{
public $id;
public $name;
public function initialize()
{
$this->hasMany(
'id',
'Store\\Models\\Parts',
'robots_id',
array(
'foreignKey' => array(
'action' => Relation::ACTION_CASCADE
)
)
);
}
}
```
```
The above code set up to delete all the referenced records (parts) if the master record (robot) is deleted.
### 生成運算(Generating Calculations)
Calculations (or aggregations) are helpers for commonly used functions of database systems such as COUNT, SUM, MAX, MIN or AVG.[*Phalcon\\Mvc\\Model*](#) allows to use these functions directly from the exposed methods.
Count examples:
```
<pre class="calibre14">```
<?php
// How many employees are?
$rowcount = Employees::count();
// How many different areas are assigned to employees?
$rowcount = Employees::count(
array(
"distinct" => "area"
)
);
// How many employees are in the Testing area?
$rowcount = Employees::count(
"area = 'Testing'"
);
// Count employees grouping results by their area
$group = Employees::count(
array(
"group" => "area"
)
);
foreach ($group as $row) {
echo "There are ", $row->rowcount, " in ", $row->area;
}
// Count employees grouping by their area and ordering the result by count
$group = Employees::count(
array(
"group" => "area",
"order" => "rowcount"
)
);
// Avoid SQL injections using bound parameters
$group = Employees::count(
array(
"type > ?0",
"bind" => array($type)
)
);
```
```
Sum examples:
```
<pre class="calibre14">```
<?php
// How much are the salaries of all employees?
$total = Employees::sum(
array(
"column" => "salary"
)
);
// How much are the salaries of all employees in the Sales area?
$total = Employees::sum(
array(
"column" => "salary",
"conditions" => "area = 'Sales'"
)
);
// Generate a grouping of the salaries of each area
$group = Employees::sum(
array(
"column" => "salary",
"group" => "area"
)
);
foreach ($group as $row) {
echo "The sum of salaries of the ", $row->area, " is ", $row->sumatory;
}
// Generate a grouping of the salaries of each area ordering
// salaries from higher to lower
$group = Employees::sum(
array(
"column" => "salary",
"group" => "area",
"order" => "sumatory DESC"
)
);
// Avoid SQL injections using bound parameters
$group = Employees::sum(
array(
"conditions" => "area > ?0",
"bind" => array($area)
)
);
```
```
Average examples:
```
<pre class="calibre14">```
<?php
// What is the average salary for all employees?
$average = Employees::average(
array(
"column" => "salary"
)
);
// What is the average salary for the Sales's area employees?
$average = Employees::average(
array(
"column" => "salary",
"conditions" => "area = 'Sales'"
)
);
// Avoid SQL injections using bound parameters
$average = Employees::average(
array(
"column" => "age",
"conditions" => "area > ?0",
"bind" => array($area)
)
);
```
```
Max/Min examples:
```
<pre class="calibre14">```
<?php
// What is the oldest age of all employees?
$age = Employees::maximum(
array(
"column" => "age"
)
);
// What is the oldest of employees from the Sales area?
$age = Employees::maximum(
array(
"column" => "age",
"conditions" => "area = 'Sales'"
)
);
// What is the lowest salary of all employees?
$salary = Employees::minimum(
array(
"column" => "salary"
)
);
```
```
### Hydration Modes
As mentioned above, resultsets are collections of complete objects, this means that every returned result is an objectrepresenting a row in the database. These objects can be modified and saved again to persistence:
```
<pre class="calibre14">```
<?php
// Manipulating a resultset of complete objects
foreach (Robots::find() as $robot) {
$robot->year = 2000;
$robot->save();
}
```
```
Sometimes records are obtained only to be presented to a user in read-only mode, in these cases it may be usefulto change the way in which records are represented to facilitate their handling. The strategy used to represent objectsreturned in a resultset is called ‘hydration mode':
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model\Resultset;
$robots = Robots::find();
// Return every robot as an array
$robots->setHydrateMode(Resultset::HYDRATE_ARRAYS);
foreach ($robots as $robot) {
echo $robot['year'], PHP_EOL;
}
// Return every robot as a stdClass
$robots->setHydrateMode(Resultset::HYDRATE_OBJECTS);
foreach ($robots as $robot) {
echo $robot->year, PHP_EOL;
}
// Return every robot as a Robots instance
$robots->setHydrateMode(Resultset::HYDRATE_RECORDS);
foreach ($robots as $robot) {
echo $robot->year, PHP_EOL;
}
```
```
Hydration mode can also be passed as a parameter of ‘find':
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model\Resultset;
$robots = Robots::find(
array(
'hydration' => Resultset::HYDRATE_ARRAYS
)
);
foreach ($robots as $robot) {
echo $robot['year'], PHP_EOL;
}
```
```
### 創建與更新記錄(Creating Updating/Records)
The method Phalcon\\Mvc\\Model::save() allows you to create/update records according to whether they already exist in the tableassociated with a model. The save method is called internally by the create and update methods of [*Phalcon\\Mvc\\Model*](#).For this to work as expected it is necessary to have properly defined a primary key in the entity to determine whether a recordshould be updated or created.
Also the method executes associated validators, virtual foreign keys and events that are defined in the model:
```
<pre class="calibre14">```
<?php
$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";
foreach ($robot->getMessages() as $message) {
echo $message, "\n";
}
} else {
echo "Great, a new robot was saved successfully!";
}
```
```
An array could be passed to “save” to avoid assign every column manually. Phalcon\\Mvc\\Model will check if there are setters implemented forthe columns passed in the array giving priority to them instead of assign directly the values of the attributes:
```
<pre class="calibre14">```
<?php
$robot = new Robots();
$robot->save(
array(
"type" => "mechanical",
"name" => "Astro Boy",
"year" => 1952
)
);
```
```
Values assigned directly or via the array of attributes are escaped/sanitized according to the related attribute data type. So you can passan insecure array without worrying about possible SQL injections:
```
<pre class="calibre14">```
<?php
$robot = new Robots();
$robot->save($_POST);
```
```
> Without precautions mass assignment could allow attackers to set any database column's value. Only use this featureif you want to permit a user to insert/update every column in the model, even if those fields are not in the submittedform.
You can set an additional parameter in ‘save' to set a whitelist of fields that only must taken into account when doingthe mass assignment:
```
<pre class="calibre14">```
<?php
$robot = new Robots();
$robot->save(
$_POST,
array(
'name',
'type'
)
);
```
```
### 創建與更新結果判斷(Create/Update with Confidence)
When an application has a lot of competition, we could be expecting create a record but it is actually updated. Thiscould happen if we use Phalcon\\Mvc\\Model::save() to persist the records in the database. If we want to be absolutelysure that a record is created or updated, we can change the save() call with create() or update():
```
<pre class="calibre14">```
<?php
$robot = new Robots();
$robot->type = "mechanical";
$robot->name = "Astro Boy";
$robot->year = 1952;
// This record only must be created
if ($robot->create() == false) {
echo "Umh, We can't store robots right now: \n";
foreach ($robot->getMessages() as $message) {
echo $message, "\n";
}
} else {
echo "Great, a new robot was created successfully!";
}
```
```
These methods “create” and “update” also accept an array of values as parameter.
### 自動生成標識列(Auto-generated identity columns)
Some models may have identity columns. These columns usually are the primary key of the mapped table. [*Phalcon\\Mvc\\Model*](#)can recognize the identity column omitting it in the generated SQL INSERT, so the database system can generate an auto-generated value for it.Always after creating a record, the identity field will be registered with the value generated in the database system for it:
```
<pre class="calibre14">```
<?php
$robot->save();
echo "The generated id is: ", $robot->id;
```
```
[*Phalcon\\Mvc\\Model*](#) is able to recognize the identity column. Depending on the database system, those columns may beserial columns like in PostgreSQL or auto\_increment columns in the case of MySQL.
PostgreSQL uses sequences to generate auto-numeric values, by default, Phalcon tries to obtain the generated value from the sequence “table\_field\_seq”,for example: robots\_id\_seq, if that sequence has a different name, the method “getSequenceName” needs to be implemented:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function getSequenceName()
{
return "robots_sequence_name";
}
}
```
```
### 存儲關系記錄(Storing related records)
Magic properties can be used to store a records and its related properties:
```
<pre class="calibre14">```
<?php
// Create an artist
$artist = new Artists();
$artist->name = 'Shinichi Osawa';
$artist->country = 'Japan';
// Create an album
$album = new Albums();
$album->name = 'The One';
$album->artist = $artist; // Assign the artist
$album->year = 2008;
// Save both records
$album->save();
```
```
Saving a record and its related records in a has-many relation:
```
<pre class="calibre14">```
<?php
// Get an existing artist
$artist = Artists::findFirst('name = "Shinichi Osawa"');
// Create an album
$album = new Albums();
$album->name = 'The One';
$album->artist = $artist;
$songs = array();
// Create a first song
$songs[0] = new Songs();
$songs[0]->name = 'Star Guitar';
$songs[0]->duration = '5:54';
// Create a second song
$songs[1] = new Songs();
$songs[1]->name = 'Last Days';
$songs[1]->duration = '4:29';
// Assign the songs array
$album->songs = $songs;
// Save the album + its songs
$album->save();
```
```
Saving the album and the artist at the same time implicitly makes use of a transaction so if anythinggoes wrong with saving the related records, the parent will not be saved either. Messages arepassed back to the user for information regarding any errors.
Note: Adding related entities by overloading the following methods is not possible:
>
> - Phalcon\\Mvc\\Model::beforeSave()
> - Phalcon\\Mvc\\Model::beforeCreate()
> - Phalcon\\Mvc\\Model::beforeUpdate()
You need to overload PhalconMvcModel::save() for this to work from within a model.
### 驗證信息(Validation Messages)
[*Phalcon\\Mvc\\Model*](#) has a messaging subsystem that provides a flexible way to output or store thevalidation messages generated during the insert/update processes.
Each message consists of an instance of the class [*Phalcon\\Mvc\\Model\\Message*](#). The set ofmessages generated can be retrieved with the method getMessages(). Each message provides extended information like the field name thatgenerated the message or the message type:
```
<pre class="calibre14">```
<?php
if ($robot->save() == false) {
foreach ($robot->getMessages() as $message) {
echo "Message: ", $message->getMessage();
echo "Field: ", $message->getField();
echo "Type: ", $message->getType();
}
}
```
```
[*Phalcon\\Mvc\\Model*](#) can generate the following types of validation messages:
TypeDescriptionPresenceOfGenerated when a field with a non-null attribute on the database is trying to insert/update a null valueConstraintViolationGenerated when a field part of a virtual foreign key is trying to insert/update a value that doesn't exist in the referenced modelInvalidValueGenerated when a validator failed because of an invalid valueInvalidCreateAttemptProduced when a record is attempted to be created but it already existsInvalidUpdateAttemptProduced when a record is attempted to be updated but it doesn't existThe method getMessages() can be overridden in a model to replace/translate the default messages generated automatically by the ORM:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function getMessages()
{
$messages = array();
foreach (parent::getMessages() as $message) {
switch ($message->getType()) {
case 'InvalidCreateAttempt':
$messages[] = 'The record cannot be created because it already exists';
break;
case 'InvalidUpdateAttempt':
$messages[] = 'The record cannot be updated because it already exists';
break;
case 'PresenceOf':
$messages[] = 'The field ' . $message->getField() . ' is mandatory';
break;
}
}
return $messages;
}
}
```
```
### 事件與事件管理器(Events and Events Manager)
Models allow you to implement events that will be thrown when performing an insert/update/delete. They help define business rules for acertain model. The following are the events supported by [*Phalcon\\Mvc\\Model*](#) and their order of execution:
OperationNameCan stop operation?ExplanationInserting/UpdatingbeforeValidationYESIs executed before the fields are validated for not nulls/empty strings or foreign keysInsertingbeforeValidationOnCreateYESIs executed before the fields are validated for not nulls/empty strings or foreign keys when an insertion operation is being madeUpdatingbeforeValidationOnUpdateYESIs executed before the fields are validated for not nulls/empty strings or foreign keys when an updating operation is being madeInserting/UpdatingonValidationFailsYES (already stopped)Is executed after an integrity validator failsInsertingafterValidationOnCreateYESIs executed after the fields are validated for not nulls/empty strings or foreign keys when an insertion operation is being madeUpdatingafterValidationOnUpdateYESIs executed after the fields are validated for not nulls/empty strings or foreign keys when an updating operation is being madeInserting/UpdatingafterValidationYESIs executed after the fields are validated for not nulls/empty strings or foreign keysInserting/UpdatingbeforeSaveYESRuns before the required operation over the database systemUpdatingbeforeUpdateYESRuns before the required operation over the database system only when an updating operation is being madeInsertingbeforeCreateYESRuns before the required operation over the database system only when an inserting operation is being madeUpdatingafterUpdateNORuns after the required operation over the database system only when an updating operation is being madeInsertingafterCreateNORuns after the required operation over the database system only when an inserting operation is being madeInserting/UpdatingafterSaveNORuns after the required operation over the database system### 模型中自定義事件(Implementing Events in the Model's class)
The easier way to make a model react to events is implement a method with the same name of the event in the model's class:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function beforeValidationOnCreate()
{
echo "This is executed before creating a Robot!";
}
}
```
```
Events can be useful to assign values before performing an operation, for example:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Products extends Model
{
public function beforeCreate()
{
// Set the creation date
$this->created_at = date('Y-m-d H:i:s');
}
public function beforeUpdate()
{
// Set the modification date
$this->modified_in = date('Y-m-d H:i:s');
}
}
```
```
### 使用自定義事件管理器(Using a custom Events Manager)
Additionally, this component is integrated with [*Phalcon\\Events\\Manager*](#),this means we can create listeners that run when an event is triggered.
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
use Phalcon\Events\Manager as EventsManager;
class Robots extends Model
{
public function initialize()
{
$eventsManager = new EventsManager();
// Attach an anonymous function as a listener for "model" events
$eventsManager->attach('model', function ($event, $robot) {
if ($event->getType() == 'beforeSave') {
if ($robot->name == 'Scooby Doo') {
echo "Scooby Doo isn't a robot!";
return false;
}
}
return true;
});
// Attach the events manager to the event
$this->setEventsManager($eventsManager);
}
}
```
```
In the example given above, the Events Manager only acts as a bridge between an object and a listener (the anonymous function).Events will be fired to the listener when ‘robots' are saved:
```
<pre class="calibre14">```
<?php
$robot = new Robots();
$robot->name = 'Scooby Doo';
$robot->year = 1969;
$robot->save();
```
```
If we want all objects created in our application use the same EventsManager, then we need to assign it to the Models Manager:
```
<pre class="calibre14">```
<?php
// Registering the modelsManager service
$di->setShared('modelsManager', function () {
$eventsManager = new \Phalcon\Events\Manager();
// Attach an anonymous function as a listener for "model" events
$eventsManager->attach('model', function ($event, $model) {
// Catch events produced by the Robots model
if (get_class($model) == 'Robots') {
if ($event->getType() == 'beforeSave') {
if ($model->name == 'Scooby Doo') {
echo "Scooby Doo isn't a robot!";
return false;
}
}
}
return true;
});
// Setting a default EventsManager
$modelsManager = new ModelsManager();
$modelsManager->setEventsManager($eventsManager);
return $modelsManager;
});
```
```
If a listener returns false that will stop the operation that is executing currently.
### 實現業務邏輯(Implementing a Business Rule)
When an insert, update or delete is executed, the model verifies if there are any methods with the names ofthe events listed in the table above.
We recommend that validation methods are declared protected to prevent that business logic implementationfrom being exposed publicly.
The following example implements an event that validates the year cannot be smaller than 0 on update or insert:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function beforeSave()
{
if ($this->year < 0) {
echo "Year cannot be smaller than zero!";
return false;
}
}
}
```
```
Some events return false as an indication to stop the current operation. If an event doesn't return anything, [*Phalcon\\Mvc\\Model*](#)will assume a true value.
### 驗證數據完整性(Validating Data Integrity)
[*Phalcon\\Mvc\\Model*](#) provides several events to validate data and implement business rules. The special “validation”event allows us to call built-in validators over the record. Phalcon exposes a few built-in validators that can be used at this stage of validation.
The following example shows how to use it:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Validator\Uniqueness;
use Phalcon\Mvc\Model\Validator\InclusionIn;
class Robots extends Model
{
public function validation()
{
$this->validate(
new InclusionIn(
array(
"field" => "type",
"domain" => array("Mechanical", "Virtual")
)
)
);
$this->validate(
new Uniqueness(
array(
"field" => "name",
"message" => "The robot name must be unique"
)
)
);
return $this->validationHasFailed() != true;
}
}
```
```
The above example performs a validation using the built-in validator “InclusionIn”. It checks the value of the field “type” in a domain list. Ifthe value is not included in the method then the validator will fail and return false. The following built-in validators are available:
NameExplanationExamplePresenceOfValidates that a field's value isn't null or empty string. This validator is automatically added based on the attributes marked as not null on the mapped table[*Example*](#)EmailValidates that field contains a valid email format[*Example*](#)ExclusionInValidates that a value is not within a list of possible values[*Example*](#)InclusionInValidates that a value is within a list of possible values[*Example*](#)NumericalityValidates that a field has a numeric format[*Example*](#)RegexValidates that the value of a field matches a regular expression[*Example*](#)UniquenessValidates that a field or a combination of a set of fields are not present more than once in the existing records of the related table[*Example*](#)StringLengthValidates the length of a string[*Example*](#)UrlValidates that a value has a valid URL format[*Example*](#)In addition to the built-in validators, you can create your own validators:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model\Validator;
use Phalcon\Mvc\Model\ValidatorInterface;
use Phalcon\Mvc\EntityInterface;
class MaxMinValidator extends Validator implements ValidatorInterface
{
public function validate(EntityInterface $model)
{
$field = $this->getOption('field');
$min = $this->getOption('min');
$max = $this->getOption('max');
$value = $model->$field;
if ($min <= $value && $value <= $max) {
$this->appendMessage(
"The field doesn't have the right range of values",
$field,
"MaxMinValidator"
);
return false;
}
return true;
}
}
```
```
> *NOTE* Up to version 2.0.4 $model must be \\Phalcon\\Mvc\\ModelInterfaceinstance (public function validate(\\Phalcon\\Mvc\\ModelInterface $model)).
Adding the validator to a model:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Customers extends Model
{
public function validation()
{
$this->validate(
new MaxMinValidator(
array(
"field" => "price",
"min" => 10,
"max" => 100
)
)
);
if ($this->validationHasFailed() == true) {
return false;
}
}
}
```
```
The idea of creating validators is make them reusable between several models. A validator can also be as simple as:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Message;
class Robots extends Model
{
public function validation()
{
if ($this->type == "Old") {
$message = new Message(
"Sorry, old robots are not allowed anymore",
"type",
"MyType"
);
$this->appendMessage($message);
return false;
}
return true;
}
}
```
```
### Avoiding SQL injections
Every value assigned to a model attribute is escaped depending of its data type. A developer doesn't need to escape manuallyeach value before storing it on the database. Phalcon uses internally the [bound parameters](http://php.net/manual/en/pdostatement.bindparam.php)capability provided by PDO to automatically escape every value to be stored in the database.
```
<pre class="calibre14">```
mysql> desc products;
+------------------+------------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+------------------+------+-----+---------+----------------+
| id | int(10) unsigned | NO | PRI | NULL | auto_increment |
| product_types_id | int(10) unsigned | NO | MUL | NULL | |
| name | varchar(70) | NO | | NULL | |
| price | decimal(16,2) | NO | | NULL | |
| active | char(1) | YES | | NULL | |
+------------------+------------------+------+-----+---------+----------------+
5 rows in set (0.00 sec)
```
```
If we use just PDO to store a record in a secure way, we need to write the following code:
```
<pre class="calibre14">```
<?php
$name = 'Artichoke';
$price = 10.5;
$active = 'Y';
$productTypesId = 1;
$sql = 'INSERT INTO products VALUES (null, :productTypesId, :name, :price, :active)';
$sth = $dbh->prepare($sql);
$sth->bindParam(':productTypesId', $productTypesId, PDO::PARAM_INT);
$sth->bindParam(':name', $name, PDO::PARAM_STR, 70);
$sth->bindParam(':price', doubleval($price));
$sth->bindParam(':active', $active, PDO::PARAM_STR, 1);
$sth->execute();
```
```
The good news is that Phalcon do this for you automatically:
```
<pre class="calibre14">```
<?php
$product = new Products();
$product->product_types_id = 1;
$product->name = 'Artichoke';
$product->price = 10.5;
$product->active = 'Y';
$product->create();
```
```
### 忽略指定列的數據(Skipping Columns)
To tell Phalcon\\Mvc\\Model that always omits some fields in the creation and/or update of records in orderto delegate the database system the assignation of the values by a trigger or a default:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
// Skips fields/columns on both INSERT/UPDATE operations
$this->skipAttributes(
array(
'year',
'price'
)
);
// Skips only when inserting
$this->skipAttributesOnCreate(
array(
'created_at'
)
);
// Skips only when updating
$this->skipAttributesOnUpdate(
array(
'modified_in'
)
);
}
}
```
```
This will ignore globally these fields on each INSERT/UPDATE operation on the whole application.If you want to ignore different attributes on different INSERT/UPDATE operations, you can specify the second parameter (boolean) - truefor replacement. Forcing a default value can be done in the following way:
```
<pre class="calibre14">```
<?php
use Phalcon\Db\RawValue;
$robot = new Robots();
$robot->name = 'Bender';
$robot->year = 1999;
$robot->created_at = new RawValue('default');
$robot->create();
```
```
A callback also can be used to create a conditional assignment of automatic default values:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
use Phalcon\Db\RawValue;
class Robots extends Model
{
public function beforeCreate()
{
if ($this->price > 10000) {
$this->type = new RawValue('default');
}
}
}
```
```
> Never use a \\Phalcon\\Db\\RawValue to assign external data (such as user input)or variable data. The value of these fields is ignored when binding parameters to the [query.So](http://query.So) it could be used to attack the application injecting SQL.
### 動態更新(Dynamic Update)
SQL UPDATE statements are by default created with every column defined in the model (full all-field SQL update).You can change specific models to make dynamic updates, in this case, just the fields that had changedare used to create the final SQL statement.
In some cases this could improve the performance by reducing the traffic between the application and the database server,this specially helps when the table has blob/text fields:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->useDynamicUpdate(true);
}
}
```
```
### 刪除記錄(Deleting Records)
The method Phalcon\\Mvc\\Model::delete() allows to delete a record. You can use it as follows:
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst(11);
if ($robot != false) {
if ($robot->delete() == false) {
echo "Sorry, we can't delete the robot right now: \n";
foreach ($robot->getMessages() as $message) {
echo $message, "\n";
}
} else {
echo "The robot was deleted successfully!";
}
}
```
```
You can also delete many records by traversing a resultset with a foreach:
```
<pre class="calibre14">```
<?php
foreach (Robots::find("type='mechanical'") as $robot) {
if ($robot->delete() == false) {
echo "Sorry, we can't delete the robot right now: \n";
foreach ($robot->getMessages() as $message) {
echo $message, "\n";
}
} else {
echo "The robot was deleted successfully!";
}
}
```
```
The following events are available to define custom business rules that can be executed when a delete operation isperformed:
OperationNameCan stop operation?ExplanationDeletingbeforeDeleteYESRuns before the delete operation is madeDeletingafterDeleteNORuns after the delete operation was madeWith the above events can also define business rules in the models:
```
<pre class="calibre14">```
<?php
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;
}
}
```
```
### 驗證失敗事件(Validation Failed Events)
Another type of events are available when the data validation process finds any inconsistency:
OperationNameExplanationInsert or UpdatenotSavedTriggered when the INSERT or UPDATE operation fails for any reasonInsert, Delete or UpdateonValidationFailsTriggered when any data manipulation operation fails### 行為(Behaviors)
Behaviors are shared conducts that several models may adopt in order to re-use code, the ORM provides an API to implementbehaviors in your models. Also, you can use the events and callbacks as seen before as an alternative to implement Behaviors with more freedom.
A behavior must be added in the model initializer, a model can have zero or more behaviors:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Behavior\Timestampable;
class Users extends Model
{
public $id;
public $name;
public $created_at;
public function initialize()
{
$this->addBehavior(
new Timestampable(
array(
'beforeCreate' => array(
'field' => 'created_at',
'format' => 'Y-m-d'
)
)
)
);
}
}
```
```
The following built-in behaviors are provided by the framework:
NameDescriptionTimestampableAllows to automatically update a model's attribute saving the datetime when a record is created or updatedSoftDeleteInstead of permanently delete a record it marks the record as deleted changing the value of a flag column### 生成時間戳(Timestampable)
This behavior receives an array of options, the first level key must be an event name indicating when the column must be assigned:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model\Behavior\Timestampable;
public function initialize()
{
$this->addBehavior(
new Timestampable(
array(
'beforeCreate' => array(
'field' => 'created_at',
'format' => 'Y-m-d'
)
)
)
);
}
```
```
Each event can have its own options, ‘field' is the name of the column that must be updated, if ‘format' is a string it will be usedas format of the PHP's function [date](http://php.net/manual/en/function.date.php), format can also be an anonymous function providing you the free to generate any kind timestamp:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model\Behavior\Timestampable;
public function initialize()
{
$this->addBehavior(
new Timestampable(
array(
'beforeCreate' => array(
'field' => 'created_at',
'format' => function () {
$datetime = new Datetime(new DateTimeZone('Europe/Stockholm'));
return $datetime->format('Y-m-d H:i:sP');
}
)
)
)
);
}
```
```
If the option ‘format' is omitted a timestamp using the PHP's function [time](http://php.net/manual/en/function.time.php), will be used.
### 軟刪除(SoftDelete)
This behavior can be used in the following way:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
use Phalcon\Mvc\Model\Behavior\SoftDelete;
class Users extends Model
{
const DELETED = 'D';
const NOT_DELETED = 'N';
public $id;
public $name;
public $status;
public function initialize()
{
$this->addBehavior(
new SoftDelete(
array(
'field' => 'status',
'value' => Users::DELETED
)
)
);
}
}
```
```
This behavior accepts two options: ‘field' and ‘value', ‘field' determines what field must be updated and ‘value' the value to be deleted.Let's pretend the table ‘users' has the following data:
```
<pre class="calibre14">```
mysql> select * from users;
+----+---------+--------+
| id | name | status |
+----+---------+--------+
| 1 | Lana | N |
| 2 | Brandon | N |
+----+---------+--------+
2 rows in set (0.00 sec)
```
```
If we delete any of the two records the status will be updated instead of delete the record:
```
<pre class="calibre14">```
<?php
Users::findFirst(2)->delete();
```
```
The operation will result in the following data in the table:
```
<pre class="calibre14">```
mysql> select * from users;
+----+---------+--------+
| id | name | status |
+----+---------+--------+
| 1 | Lana | N |
| 2 | Brandon | D |
+----+---------+--------+
2 rows in set (0.01 sec)
```
```
Note that you need to specify the deleted condition in your queries to effectively ignore them as deleted records, this behavior doesn't support that.
### 創建行為(Creating your own behaviors)
The ORM provides an API to create your own behaviors. A behavior must be a class implementing the [*Phalcon\\Mvc\\Model\\BehaviorInterface*](#)Also, Phalcon\\Mvc\\Model\\Behavior provides most of the methods needed to ease the implementation of behaviors.
The following behavior is an example, it implements the Blameable behavior which helps identify the userthat is performed operations over a model:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model\Behavior;
use Phalcon\Mvc\Model\BehaviorInterface;
class Blameable extends Behavior implements BehaviorInterface
{
public function notify($eventType, $model)
{
switch ($eventType) {
case 'afterCreate':
case 'afterDelete':
case 'afterUpdate':
$userName = // ... get the current user from session
// Store in a log the username - event type and primary key
file_put_contents(
'logs/blamable-log.txt',
$userName . ' ' . $eventType . ' ' . $model->id
);
break;
default:
/* ignore the rest of events */
}
}
}
```
```
The former is a very simple behavior, but it illustrates how to create a behavior, now let's add this behavior to a model:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Profiles extends Model
{
public function initialize()
{
$this->addBehavior(new Blameable());
}
}
```
```
A behavior is also capable of intercepting missing methods on your models:
```
<pre class="calibre14">```
<?php
use Phalcon\Tag;
use Phalcon\Mvc\Model\Behavior;
use Phalcon\Mvc\Model\BehaviorInterface;
class Sluggable extends Behavior implements BehaviorInterface
{
public function missingMethod($model, $method, $arguments = array())
{
// If the method is 'getSlug' convert the title
if ($method == 'getSlug') {
return Tag::friendlyTitle($model->title);
}
}
}
```
```
Call that method on a model that implements Sluggable returns a SEO friendly title:
```
<pre class="calibre14">```
<?php
$title = $post->getSlug();
```
```
### 使用 Traits 實現行為(Using Traits as behaviors)
Starting from PHP 5.4 you can use [Traits](http://php.net/manual/en/language.oop5.traits.php) to re-use code in your classes, this is another way to implementcustom behaviors. The following trait implements a simple version of the Timestampable behavior:
```
<pre class="calibre14">```
<?php
trait MyTimestampable
{
public function beforeCreate()
{
$this->created_at = date('r');
}
public function beforeUpdate()
{
$this->updated_at = date('r');
}
}
```
```
Then you can use it in your model as follows:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Products extends Model
{
use MyTimestampable;
}
```
```
### 獨立的列映射(Independent Column Mapping)
The ORM supports an independent column map, which allows the developer to use different column names in the model to the ones inthe table. Phalcon will recognize the new column names and will rename them accordingly to match the respective columns in the database.This is a great feature when one needs to rename fields in the database without having to worry about all the queriesin the code. A change in the column map in the model will take care of the rest. For example:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public $code;
public $theName;
public $theType;
public $theYear;
public function columnMap()
{
// Keys are the real names in the table and
// the values their names in the application
return array(
'id' => 'code',
'the_name' => 'theName',
'the_type' => 'theType',
'the_year' => 'theYear'
);
}
}
```
```
Then you can use the new names naturally in your code:
```
<pre class="calibre14">```
<?php
// Find a robot by its name
$robot = Robots::findFirst("theName = 'Voltron'");
echo $robot->theName, "\n";
// Get robots ordered by type
$robot = Robots::find(
array(
'order' => 'theType DESC'
)
);
foreach ($robots as $robot) {
echo 'Code: ', $robot->code, "\n";
}
// Create a robot
$robot = new Robots();
$robot->code = '10101';
$robot->theName = 'Bender';
$robot->theType = 'Industrial';
$robot->theYear = 2999;
$robot->save();
```
```
Take into consideration the following the next when renaming your columns:
- References to attributes in relationships/validators must use the new names
- Refer the real column names will result in an exception by the ORM
The independent column map allow you to:
- Write applications using your own conventions
- Eliminate vendor prefixes/suffixes in your code
- Change column names without change your application code
### 在結果集中操作(Operations over Resultsets)
If a resultset is composed of complete objects, the resultset is in the ability to perform operations on the records obtained in a simple manner:
### 更新關聯表記錄(Updating related records)
Instead of doing this:
```
<pre class="calibre14">```
<?php
foreach ($robots->getParts() as $part) {
$part->stock = 100;
$part->updated_at = time();
if ($part->update() == false) {
foreach ($part->getMessages() as $message) {
echo $message;
}
break;
}
}
```
```
you can do this:
```
<pre class="calibre14">```
<?php
$robots->getParts()->update(
array(
'stock' => 100,
'updated_at' => time()
)
);
```
```
‘update' also accepts an anonymous function to filter what records must be updated:
```
<pre class="calibre14">```
<?php
$data = array(
'stock' => 100,
'updated_at' => time()
);
// Update all the parts except those whose type is basic
$robots->getParts()->update($data, function ($part) {
if ($part->type == Part::TYPE_BASIC) {
return false;
}
return true;
});
```
```
### 刪除關聯表記錄(Deleting related records)
Instead of doing this:
```
<pre class="calibre14">```
<?php
foreach ($robots->getParts() as $part) {
if ($part->delete() == false) {
foreach ($part->getMessages() as $message) {
echo $message;
}
break;
}
}
```
```
you can do this:
```
<pre class="calibre14">```
<?php
$robots->getParts()->delete();
```
```
‘delete' also accepts an anonymous function to filter what records must be deleted:
```
<pre class="calibre14">```
<?php
// Delete only whose stock is greater or equal than zero
$robots->getParts()->delete(function ($part) {
if ($part->stock < 0) {
return false;
}
return true;
});
```
```
### 記錄快照(Record Snapshots)
Specific models could be set to maintain a record snapshot when they're queried. You can use this feature to implement auditing or just to know whatfields are changed according to the data queried from the persistence:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->keepSnapshots(true);
}
}
```
```
When activating this feature the application consumes a bit more of memory to keep track of the original values obtained from the [persistence.In](http://persistence.In) models that have this feature activated you can check what fields changed:
```
<pre class="calibre14">```
<?php
// Get a record from the database
$robot = Robots::findFirst();
// Change a column
$robot->name = 'Other name';
var_dump($robot->getChangedFields()); // ['name']
var_dump($robot->hasChanged('name')); // true
var_dump($robot->hasChanged('type')); // false
```
```
### 設置模式(Pointing to a different schema)
If a model is mapped to a table that is in a different schemas/databases than the default. You can use the getSchema method to define that:
如果一個模型映射到一個在非默認的schemas/數據庫中的表,你可以通過 getSchema 方法去定義它:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function getSchema()
{
return "toys";
}
}
```
```
### 設置多個數據庫(Setting multiple databases)
In Phalcon, all models can belong to the same database connection or have an individual one. Actually, when[*Phalcon\\Mvc\\Model*](#) needs to connect to the database it requests the “db” servicein the application's services container. You can overwrite this service setting it in the initialize method:
在Phalcon中,所有模型可以屬于同一個數據庫連接,也可以分屬獨立的數據庫連接。實際上,當 [*Phalcon\\Mvc\\Model*](#)需要連接數據庫的時候,它在應用服務容器內請求”db”這個服務。 可以通過在 initialize 方法內重寫這個服務的設置。
```
<pre class="calibre14">```
<?php
use Phalcon\Db\Adapter\Pdo\Mysql as MysqlPdo;
use Phalcon\Db\Adapter\Pdo\PostgreSQL as PostgreSQLPdo;
// This service returns a MySQL database
$di->set('dbMysql', function () {
return new MysqlPdo(
array(
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "invo"
)
);
});
// This service returns a PostgreSQL database
$di->set('dbPostgres', function () {
return new PostgreSQLPdo(
array(
"host" => "localhost",
"username" => "postgres",
"password" => "",
"dbname" => "invo"
)
);
});
```
```
Then, in the Initialize method, we define the connection service for the model:
然后,在 Initialize 方法內,我們為這個模型定義數據庫連接。
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->setConnectionService('dbPostgres');
}
}
```
```
But Phalcon offers you more flexibility, you can define the connection that must be used to ‘read' and for ‘write'. This is specially usefulto balance the load to your databases implementing a master-slave architecture:
另外Phalcon還提供了更多的靈活性,你可分別定義用來讀取和寫入的數據庫連接。這對實現主從架構的數據庫負載均衡非常有用。(譯者注:EvaEngine項目為使用Phalcon提供了更多的靈活性,推薦了解和使用)
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function initialize()
{
$this->setReadConnectionService('dbSlave');
$this->setWriteConnectionService('dbMaster');
}
}
```
```
The ORM also provides Horizontal Sharding facilities, by allowing you to implement a ‘shard' selectionaccording to the current query conditions:
另外ORM還可以通過根據當前查詢條件來實現一個 ‘shard' 選擇器,來實現水平切分的功能。
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
/**
* Dynamically selects a shard
*
* @param array $intermediate
* @param array $bindParams
* @param array $bindTypes
*/
public function selectReadConnection($intermediate, $bindParams, $bindTypes)
{
// Check if there is a 'where' clause in the select
if (isset($intermediate['where'])) {
$conditions = $intermediate['where'];
// Choose the possible shard according to the conditions
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');
}
}
}
// Use a default shard
return $this->getDI()->get('dbShard0');
}
}
```
```
The method ‘selectReadConnection' is called to choose the right connection, this method intercepts any newquery executed:
‘selectReadConnection' 方法用來選擇正確的數據庫連接,這個方法攔截任何新的查詢操作:
```
<pre class="calibre14">```
<?php
$robot = Robots::findFirst('id = 101');
```
```
### 記錄底層 SQL 語句(Logging Low-Level SQL Statements)
When using high-level abstraction components such as [*Phalcon\\Mvc\\Model*](#) to access a database, it isdifficult to understand which statements are finally sent to the database system. [*Phalcon\\Mvc\\Model*](#)is supported internally by [*Phalcon\\Db*](#). [*Phalcon\\Logger*](#) interactswith [*Phalcon\\Db*](#), providing logging capabilities on the database abstraction layer, thus allowing us to log SQLstatements as they happen.
```
<pre class="calibre14">```
<?php
use Phalcon\Logger;
use Phalcon\Events\Manager;
use Phalcon\Logger\Adapter\File as FileLogger;
use Phalcon\Db\Adapter\Pdo\Mysql as Connection;
$di->set('db', function () {
$eventsManager = new EventsManager();
$logger = new FileLogger("app/logs/debug.log");
// Listen all the database events
$eventsManager->attach('db', function ($event, $connection) use ($logger) {
if ($event->getType() == 'beforeQuery') {
$logger->log($connection->getSQLStatement(), Logger::INFO);
}
});
$connection = new Connection(
array(
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "invo"
)
);
// Assign the eventsManager to the db adapter instance
$connection->setEventsManager($eventsManager);
return $connection;
});
```
```
As models access the default database connection, all SQL statements that are sent to the database system will be logged in the file:
```
<pre class="calibre14">```
<?php
$robot = new Robots();
$robot->name = "Robby the Robot";
$robot->created_at = "1956-07-21";
if ($robot->save() == false) {
echo "Cannot save robot";
}
```
```
As above, the file *app/logs/db.log* will contain something like this:
```
<pre class="calibre14">```
[Mon, 30 Apr 12 13:47:18 -0500][DEBUG][Resource Id #77] INSERT INTO robots
(name, created_at) VALUES ('Robby the Robot', '1956-07-21')
```
```
### 分析 SQL 語句(Profiling SQL Statements)
Thanks to [*Phalcon\\Db*](#), the underlying component of [*Phalcon\\Mvc\\Model*](#),it's possible to profile the SQL statements generated by the ORM in order to analyze the performance of database operations. Withthis you can diagnose performance problems and to discover bottlenecks.
```
<pre class="calibre14">```
<?php
use Phalcon\Db\Profiler as ProfilerDb;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Db\Adapter\Pdo\Mysql as MysqlPdo;
$di->set('profiler', function () {
return new ProfilerDb();
}, true);
$di->set('db', function () use ($di) {
$eventsManager = new EventsManager();
// Get a shared instance of the DbProfiler
$profiler = $di->getProfiler();
// Listen all the database events
$eventsManager->attach('db', function ($event, $connection) use ($profiler) {
if ($event->getType() == 'beforeQuery') {
$profiler->startProfile($connection->getSQLStatement());
}
if ($event->getType() == 'afterQuery') {
$profiler->stopProfile();
}
});
$connection = new MysqlPdo(
array(
"host" => "localhost",
"username" => "root",
"password" => "secret",
"dbname" => "invo"
)
);
// Assign the eventsManager to the db adapter instance
$connection->setEventsManager($eventsManager);
return $connection;
});
```
```
Profiling some queries:
```
<pre class="calibre14">```
<?php
// Send some SQL statements to the database
Robots::find();
Robots::find(
array(
"order" => "name"
)
);
Robots::find(
array(
"limit" => 30
)
);
// Get the generated profiles from the profiler
$profiles = $di->get('profiler')->getProfiles();
foreach ($profiles as $profile) {
echo "SQL Statement: ", $profile->getSQLStatement(), "\n";
echo "Start Time: ", $profile->getInitialTime(), "\n";
echo "Final Time: ", $profile->getFinalTime(), "\n";
echo "Total Elapsed Time: ", $profile->getTotalElapsedSeconds(), "\n";
}
```
```
Each generated profile contains the duration in milliseconds that each instruction takes to complete as well as the generated SQL statement.
### 注入服務到模型(Injecting services into Models)
You may be required to access the application services within a model, the following example explains how to do that:
你可能需要在模型中用到應用中注入的服務,下面的例子會教你如何去做:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
class Robots extends Model
{
public function notSaved()
{
// Obtain the flash service from the DI container
$flash = $this->getDI()->getFlash();
// Show validation messages
foreach ($this->getMessages() as $message) {
$flash->error($message);
}
}
}
```
```
The “notSaved” event is triggered every time that a “create” or “update” action fails. So we're flashing the validation messagesobtaining the “flash” service from the DI container. By doing this, we don't have to print messages after each save.
每當 “create” 或者 “update” 操作失敗時會觸發 “notSave” 事件。所以我們從DI中獲取 “flash” 服務并推送確認消息。這樣的話,我們不需要每次在save之后去打印信息。
### 禁用或啟用特性(Disabling/Enabling Features)
In the ORM we have implemented a mechanism that allow you to enable/disable specific features or options globally on the fly.According to how you use the ORM you can disable that you aren't using. These options can also be temporarily disabled if required:
```
<pre class="calibre14">```
<?php
use Phalcon\Mvc\Model;
Model::setup(
array(
'events' => false,
'columnRenaming' => false
)
);
```
```
The available options are:
OptionDescriptionDefaulteventsEnables/Disables callbacks, hooks and event notifications from all the modelstruecolumnRenamingEnables/Disables the column renamingtruenotNullValidationsThe ORM automatically validate the not null columns present in the mapped tabletruevirtualForeignKeysEnables/Disables the virtual foreign keystruephqlLiteralsEnables/Disables literals in the PHQL parsertruelateStateBindingEnables/Disables late state binding of the method MvcModel::cloneResultMapfalse### 獨立的組件(Stand-Alone component)
Using <a class="calibre6 pcalibre1" href="">*Phalcon\\Mvc\\Model*</a> in a stand-alone mode can be demonstrated below:
```
<pre class="calibre14">```
<?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();
// Setup a connection
$di->set(
'db',
new Connection(
array(
"dbname" => "sample.db"
)
)
);
// Set a models manager
$di->set('modelsManager', new ModelsManager());
// Use the memory meta-data adapter or other
$di->set('modelsMetadata', new MetaData());
// Create a model
class Robots extends Model
{
}
// Use the model
echo Robots::count();
```
```
|
- [索引](# "總目錄")
- [下一頁](# "模型元數據(Models Meta-Data)") |
- [上一頁](# "使用控制器(Using Controllers)") |
- API參考
- API列表
- Abstract class Phalcon\Acl
- Abstract class Phalcon\Acl\Adapter
- Class Phalcon\Acl\Adapter\Memory
- Interface Phalcon\Acl\AdapterInterface
- Class Phalcon\Acl\Exception
- Class Phalcon\Acl\Resource
- Interface Phalcon\Acl\ResourceInterface
- Class Phalcon\Acl\Role
- Interface Phalcon\Acl\RoleInterface
- Class Phalcon\Annotations\Annotation
- Abstract class Phalcon\Annotations\Adapter
- Interface Phalcon\Annotations\AdapterInterface
- Class Phalcon\Annotations\Collection
- Class Phalcon\Annotations\Exception
- Class Phalcon\Annotations\Reader
- Interface Phalcon\Annotations\ReaderInterface
- Class Phalcon\Annotations\Reflection
- Class Phalcon\Assets\Collection
- Class Phalcon\Assets\Exception
- Interface Phalcon\Assets\FilterInterface
- Class Phalcon\Assets\Filters\Cssmin
- Class Phalcon\Assets\Filters\Jsmin
- Class Phalcon\Assets\Filters\None
- Class Phalcon\Assets\Inline
- Class Phalcon\Assets\Inline\Css
- Class Phalcon\Assets\Inline\Js
- Class Phalcon\Assets\Manager
- Class Phalcon\Assets\Resource
- Class Phalcon\Assets\Resource\Css
- Class Phalcon\Assets\Resource\Js
- Abstract class Phalcon\Cache\Backend
- Class Phalcon\Cache\Backend\Apc
- Class Phalcon\Cache\Backend\File
- Class Phalcon\Cache\Backend\Libmemcached
- Class Phalcon\Cache\Backend\Memcache
- Class Phalcon\Cache\Backend\Memory
- Class Phalcon\Cache\Backend\Mongo
- Class Phalcon\Cache\Backend\Redis
- Class Phalcon\Cache\Backend\Xcache
- Interface Phalcon\Cache\BackendInterface
- Class Phalcon\Cache\Exception
- Class Phalcon\Cache\Frontend\Base64
- Class Phalcon\Cache\Frontend\Data
- Class Phalcon\Cache\Frontend\Igbinary
- Class Phalcon\Cache\Frontend\Json
- Class Phalcon\Cache\Frontend\None
- Class Phalcon\Cache\Frontend\Output
- Interface Phalcon\Cache\FrontendInterface
- Class Phalcon\Cache\Multiple
- Class Phalcon\Cli\Router\Route
- Class Phalcon\Config
- Class Phalcon\Config\Adapter\Ini
- Class Phalcon\Config\Adapter\Json
- Class Phalcon\Config\Adapter\Php
- Class Phalcon\Config\Adapter\Yaml
- Class Phalcon\Config\Exception
- Class Phalcon\Crypt
- Class Phalcon\Crypt\Exception
- Interface Phalcon\CryptInterface
- Abstract class Phalcon\Db
- Abstract class Phalcon\Db\Adapter
- Interface Phalcon\Db\AdapterInterface
- Class Phalcon\Db\Column
- Interface Phalcon\Db\ColumnInterface
- Abstract class Phalcon\Db\Dialect
- Interface Phalcon\Db\DialectInterface
- Class Phalcon\Db\Exception
- Class Phalcon\Db\Index
- Interface Phalcon\Db\IndexInterface
- Class Phalcon\Db\Profiler
- Class Phalcon\Db\RawValue
- Class Phalcon\Db\Reference
- Interface Phalcon\Db\ReferenceInterface
- Class Phalcon\Db\Result\Pdo
- Interface Phalcon\Db\ResultInterface
- Class Phalcon\Debug
- Class Phalcon\Debug\Dump
- Class Phalcon\Debug\Exception
- Interface Phalcon\DiInterface
- Abstract class Phalcon\Dispatcher
- Interface Phalcon\DispatcherInterface
- Class Phalcon\Escaper
- Class Phalcon\Escaper\Exception
- Interface Phalcon\EscaperInterface
- Class Phalcon\Events\Event
- Interface Phalcon\Events\EventsAwareInterface
- Class Phalcon\Events\Exception
- Class Phalcon\Events\Manager
- Interface Phalcon\Events\ManagerInterface
- Class Phalcon\Exception
- Class Phalcon\Filter
- Class Phalcon\Filter\Exception
- Interface Phalcon\Filter\UserFilterInterface
- Interface Phalcon\FilterInterface
- Abstract class Phalcon\Flash
- Class Phalcon\Flash\Direct
- Class Phalcon\Flash\Exception
- Class Phalcon\Flash\Session
- Interface Phalcon\FlashInterface
- Class Phalcon\Forms\Form
- Abstract class Phalcon\Forms\Element
- Class Phalcon\Forms\Exception
- Class Phalcon\Forms\Manager
- Class Phalcon\Http\Cookie
- Class Phalcon\Http\Cookie\Exception
- Class Phalcon\Http\Request
- Class Phalcon\Http\Request\Exception
- Class Phalcon\Http\Request\File
- Interface Phalcon\Http\Request\FileInterface
- Interface Phalcon\Http\RequestInterface
- Class Phalcon\Http\Response
- Class Phalcon\Http\Response\Cookies
- Interface Phalcon\Http\Response\CookiesInterface
- Class Phalcon\Http\Response\Exception
- Class Phalcon\Http\Response\Headers
- Interface Phalcon\Http\Response\HeadersInterface
- Interface Phalcon\Http\ResponseInterface
- Class Phalcon\Image
- Abstract class Phalcon\Image\Adapter
- Class Phalcon\Image\Adapter\Imagick
- Interface Phalcon\Image\AdapterInterface
- Class Phalcon\Image\Exception
- Class Phalcon\Kernel
- Class Phalcon\Loader
- Class Phalcon\Loader\Exception
- Abstract class Phalcon\Logger
- Abstract class Phalcon\Logger\Adapter
- Class Phalcon\Logger\Adapter\File
- Class Phalcon\Logger\Adapter\Firephp
- Class Phalcon\Logger\Adapter\Stream
- Class Phalcon\Logger\Adapter\Syslog
- Interface Phalcon\Logger\AdapterInterface
- Class Phalcon\Logger\Exception
- Abstract class Phalcon\Logger\Formatter
- Interface Phalcon\Logger\FormatterInterface
- Class Phalcon\Logger\Item
- Class Phalcon\Logger\Multiple
- Class Phalcon\Mvc\Application
- Class Phalcon\Mvc\Application\Exception
- Abstract class Phalcon\Mvc\Collection
- Abstract class Phalcon\Mvc\Collection\Behavior
- Class Phalcon\Mvc\Collection\Behavior\SoftDelete
- Class Phalcon\Mvc\Collection\Behavior\Timestampable
- Interface Phalcon\Mvc\Collection\BehaviorInterface
- Class Phalcon\Mvc\Collection\Document
- Class Phalcon\Mvc\Collection\Exception
- Class Phalcon\Mvc\Collection\Manager
- Interface Phalcon\Mvc\Collection\ManagerInterface
- Interface Phalcon\Mvc\CollectionInterface
- Abstract class Phalcon\Mvc\Controller
- Interface Phalcon\Mvc\ControllerInterface
- Class Phalcon\Mvc\Dispatcher
- Class Phalcon\Mvc\Dispatcher\Exception
- Interface Phalcon\Mvc\DispatcherInterface
- Interface Phalcon\Mvc\EntityInterface
- Class Phalcon\Mvc\Micro
- Class Phalcon\Mvc\Micro\Collection
- Interface Phalcon\Mvc\Micro\CollectionInterface
- Class Phalcon\Mvc\Micro\Exception
- Class Phalcon\Mvc\Micro\LazyLoader
- Interface Phalcon\Mvc\Micro\MiddlewareInterface
- Abstract class Phalcon\Mvc\Model
- Abstract class Phalcon\Mvc\Model\Behavior
- Class Phalcon\Mvc\Model\Criteria
- Interface Phalcon\Mvc\Model\CriteriaInterface
- Class Phalcon\Mvc\Model\Exception
- Class Phalcon\Mvc\Model\Manager
- Interface Phalcon\Mvc\Model\ManagerInterface
- Class Phalcon\Mvc\Model\Message
- Interface Phalcon\Mvc\Model\MessageInterface
- Abstract class Phalcon\Mvc\Model\MetaData
- Interface Phalcon\Mvc\Model\MetaDataInterface
- Class Phalcon\Mvc\Model\Query
- Interface Phalcon\Mvc\Model\QueryInterface
- Class Phalcon\Mvc\Model\Relation
- Interface Phalcon\Mvc\Model\RelationInterface
- Interface Phalcon\Mvc\Model\ResultInterface
- Abstract class Phalcon\Mvc\Model\Resultset
- Abstract class Phalcon\Mvc\Model\Validator
- Interface Phalcon\Mvc\Model\ResultsetInterface
- Class Phalcon\Mvc\Model\Row
- Class Phalcon\Mvc\Model\Transaction
- Interface Phalcon\Mvc\Model\TransactionInterface
- Class Phalcon\Mvc\Model\ValidationFailed
- Interface Phalcon\Mvc\ModelInterface
- Interface Phalcon\Mvc\ModuleDefinitionInterface
- Class Phalcon\Mvc\Router
- Class Phalcon\Mvc\Router\Annotations
- Class Phalcon\Mvc\Router\Exception
- Class Phalcon\Mvc\Router\Group
- Interface Phalcon\Mvc\Router\GroupInterface
- Class Phalcon\Mvc\Router\Route
- Interface Phalcon\Mvc\Router\RouteInterface
- Interface Phalcon\Mvc\RouterInterface
- Class Phalcon\Mvc\Url
- Class Phalcon\Mvc\Url\Exception
- Interface Phalcon\Mvc\UrlInterface
- Class Phalcon\Mvc\User\Component
- Class Phalcon\Mvc\User\Module
- Class Phalcon\Mvc\User\Plugin
- Class Phalcon\Mvc\View
- Abstract class Phalcon\Mvc\View\Engine
- Interface Phalcon\Mvc\View\EngineInterface
- Class Phalcon\Mvc\View\Exception
- Class Phalcon\Mvc\View\Simple
- Interface Phalcon\Mvc\ViewBaseInterface
- Interface Phalcon\Mvc\ViewInterface
- Abstract class Phalcon\Paginator\Adapter
- Class Phalcon\Paginator\Adapter\Model
- Class Phalcon\Paginator\Adapter\NativeArray
- Class Phalcon\Paginator\Adapter\QueryBuilder
- Interface Phalcon\Paginator\AdapterInterface
- Class Phalcon\Paginator\Exception
- Class Phalcon\Queue\Beanstalk
- Class Phalcon\Queue\Beanstalk\Job
- Final class Phalcon\Registry
- Class Phalcon\Security
- Class Phalcon\Security\Exception
- Abstract class Phalcon\Session
- Abstract class Phalcon\Session\Adapter
- Interface Phalcon\Session\AdapterInterface
- Class Phalcon\Session\Bag
- Interface Phalcon\Session\BagInterface
- Class Phalcon\Session\Exception
- Class Phalcon\Tag
- Class Phalcon\Tag\Exception
- Abstract class Phalcon\Tag\Select
- Abstract class Phalcon\Text
- Abstract class Phalcon\Translate
- Abstract class Phalcon\Translate\Adapter
- Class Phalcon\Translate\Adapter\Csv
- Class Phalcon\Translate\Adapter\Gettext
- Class Phalcon\Translate\Adapter\NativeArray
- Interface Phalcon\Translate\AdapterInterface
- Class Phalcon\Translate\Exception
- Class Phalcon\Validation
- Class Phalcon\Validation\Exception
- Class Phalcon\Validation\Message
- Class Phalcon\Validation\Message\Group
- Interface Phalcon\Validation\MessageInterface
- Abstract class Phalcon\Validation\Validator
- Class Phalcon\Validation\Validator\Alnum
- Class Phalcon\Validation\Validator\Alpha
- Class Phalcon\Validation\Validator\Between
- Class Phalcon\Validation\Validator\Confirmation
- Class Phalcon\Validation\Validator\Digit
- Class Phalcon\Validation\Validator\Email
- Class Phalcon\Validation\Validator\ExclusionIn
- Class Phalcon\Validation\Validator\File
- Class Phalcon\Validation\Validator\Identical
- Class Phalcon\Validation\Validator\InclusionIn
- Class Phalcon\Validation\Validator\Numericality
- Class Phalcon\Validation\Validator\PresenceOf
- Class Phalcon\Validation\Validator\Regex
- Class Phalcon\Validation\Validator\StringLength
- Class Phalcon\Validation\Validator\Uniqueness
- Class Phalcon\Validation\Validator\Url
- Interface Phalcon\Validation\ValidatorInterface
- Class Phalcon\Version
- 參考手冊
- 安裝(Installation)
- 教程 1:讓我們通過例子來學習(Tutorial 1: Let’s learn by example)
- 教程 2:Introducing INVO(Tutorial 2: Introducing INVO)
- 教程 3: Securing INVO
- 教程 4: Using CRUDs
- 教程 5: Customizing INVO
- 教程 6: Vkuró
- 教程 7:創建簡單的 REST API(Tutorial 7: Creating a Simple REST API)
- 示例列表(List of examples)
- 依賴注入與服務定位器(Dependency Injection/Service Location)
- MVC 架構(The MVC Architecture)
- 使用控制器(Using Controllers)
- 使用模型(Working with Models)
- 模型元數據(Models Meta-Data)
- 事務管理(Model Transactions)
- Phalcon 查詢語言(Phalcon Query Language (PHQL))
- 緩存對象關系映射(Caching in the ORM)
- 對象文檔映射 ODM (Object-Document Mapper)
- 使用視圖(Using Views)
- 視圖助手(View Helpers)
- 資源文件管理(Assets Management)
- Volt 模版引擎(Volt: Template Engine)
- MVC 應用(MVC Applications)
- 路由(Routing)
- 調度控制器(Dispatching Controllers)
- 微應用(Micro Applications)
- 使用命名空間(Working with Namespaces)
- 事件管理器(Events Manager)
- Request Environment
- 返回響應(Returning Responses)
- Cookie 管理(Cookies Management)
- 生成 URL 和 路徑(Generating URLs and Paths)
- 閃存消息(Flashing Messages)
- 使用 Session 存儲數據(Storing data in Session)
- 過濾與清理(Filtering and Sanitizing)
- 上下文編碼(Contextual Escaping)
- 驗證(Validation)
- 表單(Forms)
- 讀取配置(Reading Configurations)
- 分頁(Pagination)
- 使用緩存提高性能(Improving Performance with Cache)
- 安全(Security)
- Encryption/Decryption
- 訪問控制列表 ACL(Access Control Lists ACL)
- 多語言支持(Multi-lingual Support)
- Universal Class Loader
- 日志記錄(Logging)
- 注釋解析器(Annotations Parser)
- 命令行應用(Command Line Applications)
- 隊列(Queueing)
- 數據庫抽象層(Database Abstraction Layer)
- 國際化(Internationalization)
- 數據庫遷移(Database Migrations)
- 調試應用程序(Debugging Applications)
- Phalcon 開發工具(Phalcon Developer Tools)
- 提高性能:下一步該做什么?(Increasing Performance: What’s next?)
- 單元測試(Unit testing)
- 授權(License)