CakePHP 2.x CookBook 中文版 第七章 模型 之 保存數據(一)

 

保存數據

CakePHP 會爲保存模型數據製作快照。準備保存的數據使用如下基本格式傳遞給模型的 save() 方法:

1 Array
2 ( 3     [ModelName] => Array
4     ( 5         [fieldname1] => 'value'
6         [fieldname2] => 'value'
7     ) 8 )

多數時候你無需擔心這種格式: CakePHP 的 FormHelper 和模型的 find 方法都用這種格式打包所有數據。如果使用其它的助手,數據也能方便地以 $this->request->data 形式使用。

下面是使用 CakePHP 模型向數據庫表存入數據的控制器動作的示例:

 1 public function edit($id) {  2     // 有表單數據被 POST?
 3     if ($this->request->is('post')) {  4         // 如果表單數據能夠通過校驗並保存...
 5         if ($this->Recipe->save($this->request->data)) {  6             // 設置 session 跳轉信息並跳轉
 7             $this->Session->setFlash('Recipe Saved!');  8             $this->redirect('/recipes');  9         } 10     } 11 
12     // 如果沒有表單數據,查找被編輯的 recipe 並將其賦值給視圖。
13     $this->set('recipe', $this->Recipe->findById($id)); 14 }

在 save 方法被調用時,在第一個參數中傳遞給它的數據,被 CakePHP 校驗機制校驗(更多信息請參見 數據校驗 一節)。 如果因爲某些原因,數據沒有被保存,檢查一下是不是沒有符合某些校驗規則。 可以通過輸出Model::$validationErrors 來 debug 這種情況。

1 if ($this->Recipe->save($this->request->data)) { 2     // "保存" 成功後的處理邏輯
3 } 4 debug($this->Recipe->validationErrors);

其它一些與保存相關的有用的模型方法:

Model::set($one, $two = null)

Model::set() 能夠用於將數據的一個或多個列放入模型的 data 數組。當使用帶有由 Model 提供的 ActiveRecord 特性的模型時很有用:

1 $this->Post->read(null, 1); 2 $this->Post->set('title', 'New title for the article'); 3 $this->Post->save();

此例展示瞭如何使用 ActiveRecord 的 set() 方法更新和保存單個列。還可以使用 set() 給多個列賦新值:

1 $this->Post->read(null, 1); 2 $this->Post->set(array( 3     'title' => 'New title',
4     'published' => false
5 )); 6 $this->Post->save();

上例將更新 thitle 和 published 列並保存到數據庫中。

Model::save(array $data = null, boolean $validate =true, array $fieldList = array())

這個方法保存數組格式的數據。第二個參數允許跳過校驗,第三個參數允許提供要保存的模型的列的列表。爲了提高安全性,可以使用 $fieldList 限制要保存的列。

註解

如果不提供 $fieldList,惡意用戶能夠向表單數據中添加附加的列(在你沒有使用 SecurityComponent 的情況下),並通過這種方法來改變原本不可以被改變的列。

save 方法還有一個替代語法:

1 save(array $data = null, array $params = array())

$params 數組可以用如下選項作爲其鍵:

  • validate 設置爲 true/false 能夠 允許/禁止 校驗。
  • fieldList 允許保存的列構成的數組。
  • callbacks 設置爲 false 將禁止回調。使用 ‘before’ 或 ‘after’ 將僅允許指定的回調。

關於模型回調的更多信息請參見 這裏

小技巧

如果你不想更新列在保存某些數據時被更新,在 $data 數組中添加 'updated' => false

一旦保存完成,可以使用模型對象的 $id 屬性獲得對象的 ID - 在創建新對象時可能會非常有用。

1 $this->Ingredient->save($newData); 2 $newIngredientId = $this->Ingredient->id;

創建或更新是通過模型的 id 列來控制的。如果設置了 $Model->id,帶有這個主鍵的記錄將被更新。 其它情況下,一條新記錄被創建:

1 // 創建新記錄: id 沒有設置或設置爲 null
2 $this->Recipe->create(); 3 $this->Recipe->save($this->request->data); 4 
5 // 更新記錄: id 被設置爲一個數字值
6 $this->Recipe->id = 2; 7 $this->Recipe->save($this->request->data);

小技巧

在循環中調用 save 時,不要忘記調用 create() 。

如果想更新一個值,而不是創建一條新記錄,必須確保向數據數組傳遞了主鍵列:

1 $data = array('id' => 10, 'title' => 'My new title'); 2 // 將更新 id 爲 10 的 Recipe 記錄
3 $this->Recipe->save($data);
Model::create(array $data = array())

這個方法爲保存新信息重置模型的狀態。 實際上它並不在數據庫中創建新記錄,而是清除預先設置的 Model::$id,並在 Model::$data 中設置基於數據庫列默認的默認值。

如果傳遞了 $data 參數(使用上面描述的數組格式),模型實例將準備保存這些數據(使用 $this->data)。

如果用 false 代替一個數組傳遞給此方法,模型實際將不根據之前沒有設置的模型結構來初始化列,而僅僅重置已經設置的列, 並且保留未設置的列。 這麼做是爲了避免更新數據庫中已經設置的列的值。

小技巧

如果想要用插入一個新行來代替更新已經存在的一行,必須先調用 create()。這樣能夠避免與回調或者其它位置中曾調用過的 save 發生衝突。

Model::saveField(string $fieldName, string$fieldValue, $validate = false)

用於保存單個列的值。在使用 saveField() 之前要先設置模型的 ID ($this->ModelName->id = $id)。在使用這個方法時,$fieldName 僅需要包含列名,不需要模型名和列。

例如,更新一條博客的標題,可以用如下方式在控制器中調用 saveField

1 $this->Post->saveField('title', 'A New Title for a New Day');

警告

在使用這個方法更新時不能停止更新列,你需要使用 save() 方法。

saveField 方法也有一個替代語法:

1 saveField(string $fieldName, string $fieldValue, array $params = array())

$params 數組可以用如下選項作爲其鍵:

  • validate 設置爲 true/false 能夠 允許/禁止 校驗。
  • callbacks 設置爲 false 將禁止回調。使用 ‘before’ 或 ‘after’ 將僅允許指定的回調。

Model::updateAll(array $fields, array $conditions)

一次調用更新一條或多條記錄。被更新的記錄通過 $conditions 數組標識,$fields 參數指定的列和值被更新。

例如,批准所有成爲會員超過一年的麪包師,調用如下的更新語句:

1 $this_year = date('Y-m-d h:i:s', strtotime('-1 year')); 2 
3 $this->Baker->updateAll( 4     array('Baker.approved' => true),
5     array('Baker.created <=' => $this_year) 6 );

小技巧

$fields 數組接受 SQL 表達式。字面值使用 Sanitize::escape() 手動引用。

註解

即使列中存在的編輯列被更新,它也不會通過 ORM 自動更新。必須手動將其加入到你想更新的數組中。

例如,關閉所有屬於指定客戶的所有門票:

1 $this->Ticket->updateAll( 2     array('Ticket.status' => "'closed'"),
3     array('Ticket.customer_id' => 453) 4 );

默認情況下,updateAll() 將自動連接支持 join 的數據庫的 belongsTo 關聯。通過臨時綁定關聯能夠防止這種連接。

Model::saveMany(array $data = null, array $options= array())

此方法用於同時保存同一模型的多行。可以帶有如下選項:

  • validate: 設置爲 false 將禁止校驗,設置爲 true 將在保存前校驗每條記錄,設置爲 ‘first’(此爲默認值) 將在任意一條被保存前檢查 全部 記錄。
  • atomic: 如果爲 true(默認),將在單個指令中保存所有記錄,如果 數據庫/表 不支持單指令需要設置爲 false。
  • fieldList: 與 Model::save() 方法的 $fieldList 參數相同。
  • deep: (自 2.1 版開始)如果設置爲 true,關聯數據也被保存,參見 saveAssociated。

爲單個模型保存多條記錄,$data 需要是數字索引的記錄數組:

1 $data = array( 2     array('title' => 'title 1'),
3     array('title' => 'title 2'),
4 );

註解

我們傳遞了數字索引代替了通常情況下 $data 包含的 Article 鍵。在保存同一模型的多條記錄時,記錄數組需要使用數字索引,而不是模型的鍵。

它也可以接受如下格式的數據:

1 $data = array( 2     array('Article' => array('title' => 'title 1')),
3     array('Article' => array('title' => 'title 2')),
4 );

如果還要保存帶有 $options['deep'] = true 的關聯數據,上面的兩個例子將類似於下面的代碼:

1 $data = array( 2     array('title' => 'title 1', 'Assoc' => array('field' => 'value')),
3     array('title' => 'title 2'),
4 ); 5 $data = array( 6     array('Article' => array('title' => 'title 1'), 'Assoc' => array('field' => 'value')),
7     array('Article' => array('title' => 'title 2')),
8 ); 9 $Model->saveMany($data, array('deep' => true));

切記,如果想用更新記錄代替創建新記錄,需要向數據行添加主鍵索引:

1 $data = array( 2     array('Article' => array('title' => 'New article')), // 創建新記錄
3     array('Article' => array('id' => 2, 'title' => 'title 2')), // 更新存在的記錄
4 );

Model::saveAssociated(array $data = null, array$options = array())

 

此方法用於一次保存多個模型關聯。可以帶有如下選項:

  • validate: 設置爲 false 將禁止校驗,設置爲 true 將在保存前校驗每條記錄,設置爲 ‘first’(此爲默認值) 將在任意一條被保存前檢查 全部 記錄。
  • atomic: 如果爲 true(默認),將在單個指令中保存所有記錄,如果 數據庫/表 不支持單指令需要設置爲 false。
  • fieldList: 與 Model::save() 方法的 $fieldList 參數相同。
  • deep:(自 2.1 版開始)如果設置爲 true,關聯數據也被保存,參見 saveAssociated。

爲了保存記錄的同時保存與其有着 hasOne 或者 belongsTo 關聯的記錄,data 數組看起來就像下面這樣:

1 $data = array( 2     'User' => array('username' => 'billy'),
3     'Profile' => array('sex' => 'Male', 'occupation' => 'Programmer'),
4 );

爲了保存記錄的同時,保存與其有着 hasMany 關聯的記錄,data 數組看起來就像下面這樣:

1 $data = array( 2     'Article' => array('title' => 'My first article'),
3     'Comment' => array( 4         array('body' => 'Comment 1', 'user_id' => 1),
5         array('body' => 'Comment 2', 'user_id' => 12),
6         array('body' => 'Comment 3', 'user_id' => 40),
7     ),
8 );

爲了保存記錄的同時保存與其有着超過兩層深度的 hasMany 關聯的記錄,data 數組看起來就像下面這樣:

 1 $data = array(  2     'User' => array('email' => '[email protected]'),
 3     'Cart' => array(  4         array(  5             'payment_status_id' => 2,
 6             'total_cost' => 250,
 7             'CartItem' => array(  8                 array(  9                     'cart_product_id' => 3,
10                     'quantity' => 1,
11                     'cost' => 100,
12                 ),
13                 array( 14                     'cart_product_id' => 5,
15                     'quantity' => 1,
16                     'cost' => 150,
17                 ) 18             ) 19         ) 20     ) 21 );

註解

如果保存成功,主模型的外鍵將被存儲在相關模型的 id 列中,例如 $this->RelatedModel->id

警告

在調用 atomic 選項設置爲 false 的 saveAssociated 方法時要小心的進行檢查,它返回的是一個數組,而不是邏輯值。

在 2.1 版更改: 現在你可以保存深層關聯的數據(用 $options['deep'] = true 設置)。

爲了保存記錄的同時,保存與其有 hasMany 關聯的相關記錄及深層關聯的 Comment belongsTo User 數據,data 數組看起來就像下面這樣::

1 $data = array( 2     'Article' => array('title' => 'My first article'),
3     'Comment' => array( 4         array('body' => 'Comment 1', 'user_id' => 1),
5         array('body' => 'Save a new user as well', 'User' => array('first' => 'mad', 'last' => 'coder')),
6     ),
7 );

並用如下語句進行保存:

1 $Article->saveAssociated($data, array('deep' => true));

在 2.1 版更改: Model::saveAll() 和同族方法現在支持爲多個模型傳遞 fieldList

爲多個模型傳遞 fieldList 的例子:

1 $this->SomeModel->saveAll($data, array( 2     'fieldList' => array( 3         'SomeModel' => array('field_1'),
4         'AssociatedModel' => array('field_2', 'field_3') 5     ) 6 ));

fieldList 是一個以模型別名爲鍵,以列構成的數組作爲值的數組。 模型名如同在被保存的數據中那樣,不能嵌套。

Model::saveAll(array $data = null, array $options =array())

saveAll 函數只是 savaMany 和 saveAssociated 方法的包裝器。它檢查數據並且決定執行哪種數據保存類型。它查看數據並決定執行哪種類型的保存。如果數據是數字索引數組,saveMany 被調用,否則 saveAssociated 被調用。

此函數的選項與前面的兩個函數相同,並向後兼容。推薦根據實際情況使用 saveMany 或 saveAssociated

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章