[TOC]
>[info] 請注意,如果您使用的是PHP 7提供的Mongo驅動程序,則ODM將不適合您。有一個孵化器適配器,但必須重寫所有Mongo代碼(新的Bson類型而不是數組,沒有MongoId,沒有MongoDate等)。請確保在升級到PHP 7和/或Phalcon 3+之前測試代碼
# ODM(對象文檔映射器)
除了能夠在關系數據庫中映射表之外,Phalcon還可以映射來自NoSQL數據庫的文檔。ODM提供CRUD功能,事件,其他服務的驗證。
由于缺少SQL查詢和規劃器,NoSQL數據庫可以使用Phalcon方法看到性能的真正改進。此外,沒有SQL構建減少SQL注入的可能性。
支持以下NoSQL數據庫:
|名稱 | 描述 |
| ---------------------------------- | -------------------------------------------------------------------- |
| [MongoDB](http://www.mongodb.org/) | MongoDB是一個可擴展的高性能開源NoSQL數據庫。|
## 創建模型
模型是從`Phalcon\Mvc\Collection`擴展的類。它必須放在models目錄中。模型文件必須包含單個類;它的類名應該是駝峰表示法:
```php
<?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
}
```
默認情況下,`Robots` 將參考集合`robots`。如果要為映射集合手動指定其他名稱,可以使用`setSource()`方法:
```php
<?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setSource('the_robots');
}
}
```
## 了解文檔到對象
模型的每個實例都代表集合中的文檔。您可以通過讀取對象屬性輕松訪問集合數據。例如,對于帶有文檔的集合`robots`:
```bash
$ mongo test
MongoDB shell version: 1.8.2
connecting to: test
> db.robots.find()
{ '_id' : ObjectId('508735512d42b8c3d15ec4e1'), 'name' : 'Astro Boy', 'year' : 1952,
'type' : 'mechanical' }
{ '_id' : ObjectId('5087358f2d42b8c3d15ec4e2'), 'name' : 'Bender', 'year' : 1999,
'type' : 'mechanical' }
{ '_id' : ObjectId('508735d32d42b8c3d15ec4e3'), 'name' : 'Wall-E', 'year' : 2008 }
>
```
## 命名空間中的模型
命名空間可用于避免類名沖突。在這種情況下,有必要使用`setSource()`方法指示相關集合的名稱:
```php
<?php
namespace Store\Toys;
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setSource('robots');
}
}
```
您可以通過其ID找到某個文檔,然后打印其名稱:
```php
<?php
// Find record with _id = '5087358f2d42b8c3d15ec4e2'
$robot = Robots::findById('5087358f2d42b8c3d15ec4e2');
// Prints 'Bender'
echo $robot->name;
```
記錄在內存中后,您可以對其數據進行修改,然后保存更改:
```php
<?php
$robot = Robots::findFirst(
[
[
'name' => 'Astro Boy',
]
]
);
$robot->name = 'Voltron';
$robot->save();
```
## 設置連接
從服務容器中檢索連接。默認情況下,Phalcon嘗試在名為`mongo`的服務中查找連接:
```php
<?php
// 與localhost的簡單數據庫連接
$di->set(
'mongo',
function () {
$mongo = new MongoClient();
return $mongo->selectDB('store');
},
true
);
// 連接到域套接字,回退到localhost連接
$di->set(
'mongo',
function () {
$mongo = new MongoClient(
'mongodb:///tmp/mongodb-27017.sock,localhost:27017'
);
return $mongo->selectDB('store');
},
true
);
```
## 查找文檔
由于 `Phalcon\Mvc\Collection` 依賴于Mongo PHP擴展,因此您可以使用相同的工具來查詢文檔并將它們透明地轉換為模型實例:
```php
<?php
// 那里有多少個機器人?
$robots = Robots::find();
echo 'There are ', count($robots), "\n";
// 有多少個機械機器人?
$robots = Robots::find(
[
[
'type' => 'mechanical',
]
]
);
echo 'There are ', count($robots), "\n";
// 獲取并打印按名稱向上排序的機械機器人
$robots = Robots::find(
[
[
'type' => 'mechanical',
],
'sort' => [
'name' => 1,
],
]
);
foreach ($robots as $robot) {
echo $robot->name, "\n";
}
// 按名稱訂購前100臺機械機器人
$robots = Robots::find(
[
[
'type' => 'mechanical',
],
'sort' => [
'name' => 1,
],
'limit' => 100,
]
);
foreach ($robots as $robot) {
echo $robot->name, "\n";
}
```
您還可以使用`findFirst()`方法僅獲取與給定條件匹配的第一條記錄:
```php
<?php
// 什么是機器人集合的第一個機器人?
$robot = Robots::findFirst();
echo 'The robot name is ', $robot->name, "\n";
// 機器人集合中第一臺機械機器人是什么?
$robot = Robots::findFirst(
[
[
'type' => 'mechanical',
]
]
);
echo 'The first mechanical robot name is ', $robot->name, "\n";
```
`find()` 和 `findFirst()` 方法都接受指定搜索條件的關聯數組:
```php
<?php
// 第一個機器人,其中type ='mechanical',year ='1999'
$robot = Robots::findFirst(
[
'conditions' => [
'type' => 'mechanical',
'year' => '1999',
],
]
);
// 所有虛擬機器人按名稱向下排序
$robots = Robots::find(
[
'conditions' => [
'type' => 'virtual',
],
'sort' => [
'name' => -1,
],
]
);
// 使用where條件查找所有擁有4個以上朋友的機器人
$robots = Robots::find(
[
'conditions' => [
'$where' => 'this.friends.length > 4',
]
]
);
```
可用的查詢選項包括:
| 參數 | 描述 | 示例 |
| ------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
| `conditions` | 搜索查找操作的條件。用于僅提取滿足指定標準的記錄。默認情況下,`Phalcon\Model`假定第一個參數是條件。 | `'conditions' => array('$gt' => 1990)` |
| `fields` | 返回特定列而不是集合中的完整字段。使用此選項時,將返回不完整的對象 | `'fields' => array('name' => true)` |
| `sort` | 它用于對結果集進行排序。使用一個或多個字段作為數組中的每個元素,1表示向上排序,-1表示向下排序 | `'sort' => array('name' => -1, 'status' => 1)` |
| `limit` | 將查詢結果限制為某個范圍的結果 | `'limit' => 10` |
| `skip` | 跳過一些結果 | `'skip' => 50` |
如果您有使用SQL數據庫的經驗,則可能需要檢查[SQL到Mongo Mapping Chart](http://www.php.net/manual/en/mongo.sqltomongo.php)。
## 查詢特定字段
要使用Phalcon ODM從MongoDB數據庫查詢特定字段的特定字段,您只需要:
```php
$myRobots = Robots:find(
[
'fields' => ['name' => 1]
]
];
```
上面的`find()`只返回一個`name`。它也可以與`condition`結合:
```php
$myRobots = Robots:find(
[
['type' => 'maid'],
'fields' => ['name' => 1]
]
];
```
上面的示例返回`type = 'maid'`的機器人名稱。
## 聚合
模型可以使用Mongo提供的[聚合框架](http://docs.mongodb.org/manual/applications/aggregation/)返回計算。無需使用MapReduce即可計算聚合值。使用此選項可以輕松執行諸如總計或平均字段值之類的任務:
```php
<?php
$data = Article::aggregate(
[
[
'\$project' => [
'category' => 1,
],
],
[
'\$group' => [
'_id' => [
'category' => '\$category'
],
'id' => [
'\$max' => '\$_id',
],
],
],
]
);
```
## 創建更新/記錄
`Phalcon\Mvc\Collection::save()` 方法允許您根據文檔是否已存在于與模型關聯的集合中來創建/更新文檔。`save()` 方法由 `Phalcon\Mvc\Collection`的create和update方法在內部調用。
此方法還執行模型中定義的關聯驗證器和事件:
```php
<?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";
$messages = $robot->getMessages();
foreach ($messages as $message) {
echo $message, "\n";
}
} else {
echo 'Great, a new robot was saved successfully!';
}
```
使用驅動程序創建的[MongoId](http://www.php.net/manual/en/class.mongoid.php)對象自動更新`_id`屬性:
```php
<?php
$robot->save();
echo 'The generated id is: ', $robot->getId();
```
<a name='validation-messages'></a>
### 驗證消息
`Phalcon\Mvc\Collection` 有一個消息傳遞子系統,它提供了一種靈活的方式來輸出或存儲插入/更新過程中生成的驗證消息。
每條消息都包含 `Phalcon\Mvc\Model\Message`類的實例。可以使用 `getMessages()`方法檢索生成的消息集。
每條消息都提供擴展信息,例如生成消息的字段名稱或消息類型:
```php
<?php
if ($robot->save() === false) {
$messages = $robot->getMessages();
foreach ($messages as $message) {
echo 'Message: ', $message->getMessage();
echo 'Field: ', $message->getField();
echo 'Type: ', $message->getType();
}
}
```
### 驗證事件和事件管理器
模型允許您實現在執行插入或更新時將拋出的事件。它們有助于為特定模型定義業務規則。以下是`Phalcon\Mvc\Collection`支持的事件及其執行順序:
| 操作 | 名稱 | Can stop operation? | 說明 |
| ------------------ | -------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------ |
| Inserting/Updating | `beforeValidation` | YES | 在驗證過程之前執行并最終插入/更新到數據庫 |
| Inserting | `beforeValidationOnCreate` | YES | 僅在進行插入操作時才在驗證過程之前執行 |
| Updating | `beforeValidationOnUpdate` | YES | 在進行更新操作時,在字段驗證非空值或外鍵之前執行 |
| Inserting/Updating | `onValidationFails` | YES (already stopped) | 僅在進行插入操作時才在驗證過程之前執行 |
| Inserting | `afterValidationOnCreate` | YES | 在進行插入操作時在驗證過程之后執行 |
| Updating | `afterValidationOnUpdate` | YES | 在進行更新操作時,在驗證過程之后執行|
| Inserting/Updating | `afterValidation` | YES | 在驗證過程后執行 |
| Inserting/Updating | `beforeSave` | YES | 在數據庫系統上執行所需的操作之前運行|
| Updating | `beforeUpdate` | YES | 僅在進行更新操作時才在數據庫系統上執行所需操作之前運行 |
| Inserting | `beforeCreate` | YES | 僅在進行插入操作時才在數據庫系統上執行所需操作之前運行 |
| Updating | `afterUpdate` | NO | 僅在進行更新操作時,在數據庫系統上執行所需操作后運行 |
| Inserting | `afterCreate` | NO | 僅在進行插入操作時,在數據庫系統上執行所需操作后運行|
| Inserting/Updating | `afterSave` | NO | 在數據庫系統上執行所需的操作后運行|
要使模型對事件做出反應,我們必須實現一個具有相同事件名稱的方法:
```php
<?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function beforeValidationOnCreate()
{
echo 'This is executed before creating a Robot!';
}
}
```
在執行操作之前,事件可用于分配值,例如:
```php
<?php
use Phalcon\Mvc\Collection;
class Products extends Collection
{
public function beforeCreate()
{
// 設置創建日期
$this->created_at = date('Y-m-d H:i:s');
}
public function beforeUpdate()
{
// 設置修改日期
$this->modified_in = date('Y-m-d H:i:s');
}
}
```
此外,該組件與Phalcon事件管理器(`Phalcon\Events\Manager`)集成,這意味著我們可以創建在觸發事件時運行的偵聽器。
```php
<?php
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
$eventsManager = new EventsManager();
// 附加匿名函數作為“模型”事件的偵聽器
$eventsManager->attach(
'collection:beforeSave',
function (Event $event, $robot) {
if ($robot->name === 'Scooby Doo') {
echo "Scooby Doo isn't a robot!";
return false;
}
return true;
}
);
$robot = new Robots();
$robot->setEventsManager($eventsManager);
$robot->name = 'Scooby Doo';
$robot->year = 1969;
$robot->save();
```
在上面給出的示例中,EventsManager僅充當對象和偵聽器(匿名函數)之間的橋梁。如果我們希望在我們的應用程序中創建的所有對象使用相同的EventsManager,那么我們需要將它分配給Models Manager:
```php
<?php
use Phalcon\Events\Event;
use Phalcon\Events\Manager as EventsManager;
use Phalcon\Mvc\Collection\Manager as CollectionManager;
// 注冊collectionManager服務
$di->set(
'collectionManager',
function () {
$eventsManager = new EventsManager();
// 附加匿名函數作為“模型”事件的偵聽器
$eventsManager->attach(
'collection:beforeSave',
function (Event $event, $model) {
if (get_class($model) === 'Robots') {
if ($model->name === 'Scooby Doo') {
echo "Scooby Doo isn't a robot!";
return false;
}
}
return true;
}
);
// 設置默認的EventsManager
$modelsManager = new CollectionManager();
$modelsManager->setEventsManager($eventsManager);
return $modelsManager;
},
true
);
```
### 實現業務規則
執行插入,更新或刪除時,模型將驗證是否存在具有上表中列出的事件名稱的任何方法。
我們建議聲明`protected`的驗證方法,以防止公開暴露業務邏輯實現。
以下示例實現了一個事件,該事件在更新或插入時驗證年份不能小于0:
```php
<?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
protected function beforeSave()
{
if ($this->year < 0) {
echo 'Year cannot be smaller than zero!';
return false;
}
}
}
```
某些事件返回false作為停止當前操作的指示。如果事件沒有返回任何內容,`Phalcon\Mvc\Collection`將采用true值。
### 驗證數據完整性
`Phalcon\Mvc\Collection` 提供了幾個事件來驗證數據和實現業務規則。特殊驗證事件允許我們在記錄上調用內置驗證器。Phalcon公開了一些可在此驗證階段使用的內置驗證器。
以下示例顯示了如何使用它:
```php
<?php
use Phalcon\Mvc\Collection;
use Phalcon\Validation;
use Phalcon\Validation\Validator\InclusionIn;
use Phalcon\Validation\Validator\Numericality;
class Robots extends Collection
{
public function validation()
{
$validation = new Validation();
$validation->add(
'type',
new InclusionIn(
[
'message' => 'Type must be: mechanical or virtual',
'domain' => [
'Mechanical',
'Virtual',
],
]
)
);
$validation->add(
'price',
new Numericality(
[
'message' => 'Price must be numeric'
]
)
);
return $this->validate($validation);
}
}
```
上面的示例使用內置驗證器`InclusionIn`執行驗證。它檢查字段`type`的值是否在`domain`列表中。如果該值未包含在列表中,則驗證器將失敗并返回false。
>[warning] 有關驗證器的更多信息,請參閱[驗證文檔]。
## 刪除記錄
`Phalcon\Mvc\Collection::delete()` 方法允許您刪除文檔。
您可以按如下方式使用它:
```php
<?php
$robot = Robots::findFirst();
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 robot was deleted successfully!';
}
}
```
您還可以通過使用`foreach`循環遍歷結果集來刪除許多文檔:
```php
<?php
$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!';
}
}
```
以下事件可用于定義在執行刪除操作時可以執行的自定義業務規則:
| 操作 | 名稱 | Can stop operation? | 說明 |
| --------- | -------------- | ------------------- | ---------------------------------------- |
| Deleting | `beforeDelete` | YES | 在執行刪除操作之前運行 |
| Deleting | `afterDelete` | NO | 刪除操作完成后運行 |
## 驗證失敗的事件
當數據驗證過程發現任何不一致時,可以使用另一種類型的事件:
| 操作 | 名稱 | 說明 |
| ------------------------ | ------------------- | --------------------------------------------------------------- |
| Insert or Update | `notSave` | 插入/更新操作因任何原因失敗時觸發 |
| Insert, Delete or Update | `onValidationFails` | 任何數據操作操作失敗時觸發 |
## 隱式ID與用戶主鍵
默認情況下,`Phalcon\Mvc\Collection` 假定使用[MongoIds](http://www.php.net/manual/en/class.mongoid.php)自動生成 `_id` 屬性。 如果模型使用自定義主鍵,則可以覆蓋此行為
```php
<?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->useImplicitObjectIds(false);
}
}
```
## 設置多個數據庫
在Phalcon中,所有模型都可以共享相同的數據庫連接或為每個模型指定連接。實際上,當`Phalcon\Mvc\Collection` 需要連接到數據庫時,它會在應用程序的服務容器中請求 `mongo` 服務。 您可以通過在 `initialize()`方法中設置它來覆蓋此服務:
```php
<?php
// 此服務返回192.168.1.100的mongo數據庫
$di->set(
'mongo1',
function () {
$mongo = new MongoClient(
'mongodb://scott:nekhen@192.168.1.100'
);
return $mongo->selectDB('management');
},
true
);
// 此服務在localhost返回mongo數據庫
$di->set(
'mongo2',
function () {
$mongo = new MongoClient(
'mongodb://localhost'
);
return $mongo->selectDB('invoicing');
},
true
);
```
然后,在`initialize()` 方法中,我們定義模型的連接服務:
```php
<?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function initialize()
{
$this->setConnectionService('mongo1');
}
}
```
## 將服務注入模型
您可能需要訪問模型中的應用程序服務,以下示例說明如何執行此操作:
```php
<?php
use Phalcon\Mvc\Collection;
class Robots extends Collection
{
public function notSave()
{
// 從DI容器中獲取閃存服務
$flash = $this->getDI()->getShared('flash');
$messages = $this->getMessages();
// 顯示驗證消息
foreach ($messages as $message) {
$flash->error(
(string) $message
);
}
}
}
```
只要創建或更新操作失敗,就會觸發`notSave`事件。 我們正在刷新驗證消息,從DI容器中獲取閃存服務。通過這樣做,我們不必在每次保存后打印消息。
- 常規
- Welcome
- 貢獻
- 生成回溯
- 測試重現
- 單元測試
- 入門
- 安裝
- Web服務器設置
- WAMP
- XAMPP
- 教程
- 基礎教程
- 教程:創建一個簡單的REST API
- 教程:V?kuró
- 提升性能
- 教程:INVO
- 開發環境
- Phalcon Compose (Docker)
- Nanobox
- Phalcon Box (Vagrant)
- 開發工具
- Phalcon開發者工具的安裝
- Phalcon開發者工具的使用
- 調試應用程序
- 核心
- MVC應用
- 微應用
- 創建命令行(CLI)應用程序
- 依賴注入與服務定位
- MVC架構
- 服務
- 使用緩存提高性能
- 讀取配置
- 上下文轉義
- 類加載器
- 使用命名空間
- 日志
- 隊列
- 數據庫
- 數據庫抽象層
- Phalcon查詢語言(PHQL)
- ODM(對象文檔映射器)
- 使用模型
- 模型行為
- ORM緩存
- 模型事件
- 模型元數據
- 模型關系
- 模型事務
- 驗證模型
- 數據庫遷移
- 分頁
- 前端
- Assets管理
- 閃存消息
- 表單
- 圖像
- 視圖助手(標簽)
- 使用視圖
- Volt:模板引擎
- 業務邏輯
- 訪問控制列表(ACL)
- 注解解析器
- 控制器
- 調度控制器
- 事件管理器
- 過濾與清理
- 路由
- 在session中存儲數據
- 生成URL和路徑
- 驗證
- HTTP
- Cookies管理
- 請求環境
- 返回響應
- 安全
- 加密/解密
- 安全
- 國際化
- 國際化
- 多語言支持