保存相關模型數據 (HABTM)
通過 hasOne、belongsTo、hasMany 保存有關聯的模型是非常簡單的: 只需要將關聯模型的 ID 填入外鍵列。 填完之後,只要調用模型上的 save() 方法,一切就都被正確的串連起來了。 下面是準備傳遞給 Tag 模型的 save() 方法的數據數組格式的示例:
1 Array 2 ( 3 [Recipe] => Array 4 ( 5 [id] => 42 6 ) 7 [Tag] => Array 8 ( 9 [name] => Italian 10 ) 11 )
也可以在 saveAll() 中使用這種格式保存多條記錄和與它們有 HABTM 關聯的的模型,格式如下:
1 Array 2 ( 3 [0] => Array 4 ( 5 [Recipe] => Array 6 ( 7 [id] => 42 8 ) 9 [Tag] => Array 10 ( 11 [name] => Italian 12 ) 13 ) 14 [1] => Array 15 ( 16 [Recipe] => Array 17 ( 18 [id] => 42 19 ) 20 [Tag] => Array 21 ( 22 [name] => Pasta 23 ) 24 ) 25 [2] => Array 26 ( 27 [Recipe] => Array 28 ( 29 [id] => 51 30 ) 31 [Tag] => Array 32 ( 33 [name] => Mexican 34 ) 35 ) 36 [3] => Array 37 ( 38 [Recipe] => Array 39 ( 40 [id] => 17 41 ) 42 [Tag] => Array 43 ( 44 [name] => American (new) 45 ) 46 ) 47 )
將上面的數組傳遞給 saveAll() 將創建所包含的 tag ,每個都與它們各自的 recipe 關聯。
作爲示例,我們建立了創建新 tag 和運行期間生成與 recipe 關聯的正確數據數組的表單。
這個簡單的表單如下:(我們假定 $recipe_id 已經設置了):
1 <?php echo $this->Form->create('Tag'); ?> 2 <?php echo $this->Form->input( 3 'Recipe.id', 4 array('type' => 'hidden', 'value' => $recipe_id) 5 ); ?> 6 <?php echo $this->Form->input('Tag.name'); ?> 7 <?php echo $this->Form->end('Add Tag'); ?>
在這個例子中,你能看到 Recipe.id hidden 域,其值被設置爲我們的 tag 想要連接的 recipe 的 ID。
當在控制器中調用 save() 方法,它將自動將 HABTM 數據保存到數據庫:
1 public function add() { 2 // 保存關聯 3 if ($this->Tag->save($this->request->data)) { 4 // 保存成功後要做的事情 5 } 6 }
這段代碼將創建一個新的 Tag 並與 Recipe 相關聯,其 ID 由 $this->request->data['Recipe']['id'] 設置。
某些情況下,我們可能希望呈現的關聯數據能夠包含下拉 select 列表。數據可能使用 find('list') 從模型中取出並且賦給用模型名命名的視圖變量。 同名的 input 將自動把數據放進 <select> :
1 // 控制器中的代碼: 2 $this->set('tags', $this->Recipe->Tag->find('list'));
1 // 視圖中的代碼: 2 $this->Form->input('tags');
更可能的情形是一個 HABTM 關係包含一個允許多選的 <select>。例如,一個 Recipe 可能被賦了多個 Tag。在這種情況下,數據以相同的方式從模型中取出,但是表單 input 定義稍有不同。tag 的命名使用 ModelName 約定:
1 // 控制器中的代碼: 2 $this->set('tags', $this->Recipe->Tag->find('list'));
1 // 視圖中的代碼: 2 $this->Form->input('Tag');
使用上面這段代碼,將建立可多選的下拉列表(select),允許多選自動被保存到已添加或已保存到數據庫中的 Recipe。
當 HABTM 變得複雜時怎麼辦?
默認情況下,Cake 在保存 HABTM 關係時,會先刪除連接表中的所有行。 例如,有一個擁有10個 Children 關聯的 Club。帶着2個 children 更新 Club。Club 將只有2個 Children,而不是12個。
要注意,如果想要向帶有 HABTM 的連接表添加更多的列(建立時間或者元數據)是可能的,重要的是要明白你有一個簡單的選項。
兩個模型間的 HasAndBelongsToMany 關聯實際上是同時擁有 hasMany 和 belongsTo 關聯的三個模型關係的簡寫。
考慮下面的例子:
Child hasAndBelongsToMany Club
另一個方法是添加一個 Membership 模型:
Child hasMany Membership
Membership belongsTo Child, Club
Club hasMany Membership.
這兩個例子幾乎是相同的。它們在數據庫中使用了命名相同的 amount 列,模型中的 amount 也是相同的。最重要的不同是 “join” 表命名不同,並且其行爲更具可預知性。
小技巧
當連接表包含外鍵以外的擴展列時,通過將數組的 'unique' 設置爲 “‘keepExisting’”,能夠防止丟失擴展列的值。同樣,可以認爲設置 ‘unique’ => true,在保存操作過程中不會丟失擴展列的數據。參見 HABTM association arrays。
不過,更多情況下,爲連接表建立一個模型,並像上面的例子那樣設置 hasMany、belongsTo 關聯,代替使用 HABTM 關聯,會更簡單。
數據表
雖然 CakePHP 可以有非數據庫驅動的數據源,但多數時候,都是有數據庫驅動的。 CakePHP 被設計成可以與 MySQL、MSSQL、Oracle、PostgreSQL 和其它數據庫一起工作。 你可以創建你平時所用的數據庫系統的表。在創建模型類時,模型將自動映射到已經建立的表上。表名被轉換爲複數小寫,多個單詞的表名的單詞用下劃線間隔。例如,名爲 Ingredient 的模型對應的表名爲 ingredients。名爲 EventRegistration 的模型對應的表名爲 event_registrations。CakePHP 將檢查表來決定每個列的數據類型,並使用這些信息自動化各種特性,比如視圖中輸出的表單域。列名被轉換爲小寫並用下劃線間隔。
使用 created 和 modified 列
通過在數據庫表中定義 created 和 modified 列作爲 datetime 列,CakePHP 能夠識別這些域並自動在其中填入記錄在數據庫中創建的時間和保存的時間(除非被保存的數據中已經包含了這些域的值)。
在記錄最初添加時,created 和 modified 列將被設置爲當前日期和時間。當已經存在的記錄被保存時,modified 列將被更新至當前日期和時間。
如果在 Model::save() 之前 $this->data 中包含了 updated、created、modified 數據(例如 Model::read 或者 Model::set),那麼這些值將從 $this->data 中獲取,並且不自動更新。 或者使用 unset($this->data['Model']['modified']) 等方法。總是可以覆蓋 Model::save() 方法來做這件事:
1 class AppModel extends Model { 2 3 public function save($data = null, $validate = true, $fieldList = array()) { 4 // 在每個保存操作前清除 modified 域值: 5 $this->set($data); 6 if (isset($this->data[$this->alias]['modified'])) { 7 unset($this->data[$this->alias]['modified']); 8 } 9 return parent::save($this->data, $validate, $fieldList); 10 } 11 12 }