2008年01月15日
[PHP-ZF] 初探 Zend_Db_Table Relationships (三)
前面我已經知道如何設定 Zend_Db_Table 的關連,這次來記一下串聯刪除 (cascading delete) 。
cascading delete 主要的目的是這樣的:假設 a 資料表和 b 資料表間有關連 (a.id = b.a_id) ,而當 a 資料表的某紀錄被刪除後,會使得 b 資料表中的相關紀錄失去作用而需要一併刪除。多數主流的資料庫系統都會內建類似的功能,例如 Oracle 、 MSSQL 及 PostgreSQL 等。但像是 MySQL 4.x 以前 的 MyISAM 格式並沒有提供 foreign key 和 trigger ,所以也沒辦法內建 Cascading Delete 時,這時只好從程式裡來著手。
不過有一點要小心,就算資料庫系統有支援 cascading delete ,也必須在資料表定義 foreign key 時設定 cascading delete 的相關指令 (通常是 ON DELETE CASCADE) ;不然資料表還是不會知道要去刪掉對應的紀錄。
註:以上資訊有錯誤的話,煩請大家不吝指正。
延續上一次的程式碼,這次我要為它們加入 cascading delete 的功能。
範例下載
不提供了,請下載上次的範例照以下說明修改吧。
說明
刪除文章時同時刪除文章與標籤之間的關連
首先我們在 Articles 這個類別中,加入一個 $_dependentTables 屬性:
<?php
class Articles extends Zend_Db_Table_Abstract
{
// ... 略 ...
protected $_dependentTables = array('ArticlesTags');
}
這個 $_dependentTables 必須給它一個陣列,這個陣列要包含你要串聯刪除的資料表類別名稱 (此例即為 ArticlesTags 這個類別) 。
接下來打開 ArticlesTags 這個類別,在原本的 $_referenceMap['Article'] 關連中,再加入一個 onDelete 索引:
<?php
class ArticlesTags extends Zend_Db_Table_Abstract
{
protected $_referenceMap = array(
// ... 略 ...
'Article' => array(
'columns' => array('article_id'),
'refTableClass' => 'Articles',
'refColumns' => 'id',
'onDelete' => self::CASCADE,
),
);
}
onDelete 的值固定就是用 self::CASCADE (其他值就不會動了) 。
註:別忘記原來 $_referenceMap['Article'] 所指定的意思,它是指 articles_tags 這個資料表的 articles_id (columns) 這個欄位要對應到 Articles (refTableClass) 這個資料表類別的 id 欄位 (refColumns) 。
這樣設定好後,就完成串聯刪除的動作了,很簡單吧?不過有一點非常重要:那就是一定要用 Zend_Db_Table_Row 的 delete 方法來刪除紀錄,才能啟動串聯刪除!
舉個例子:假設現在有兩篇文章,標題分別是 jQuery 和 Prototype ;現在我為它們都下了 javascript 和 ajax 兩個標籤。現在資料表紀錄如下:
| id | category_id | title |
|---|---|---|
| 3 | 2 | jQuery |
| 4 | 2 | Prototype |
| id | name |
|---|---|
| 3 | javascript |
| 4 | ajax |
| article_id | category_id |
|---|---|
| 3 | 3 |
| 3 | 4 |
| 4 | 3 |
| 4 | 4 |
註:請依照上面的例子,先在 articles 、 tags 及 artices_tags 三個資料表分別加入相關的紀錄。
接下來在 IndexController 中,加入一個 deleteAction 方法:
<?php
class IndexController extends Zend_Controller_Action
{
// ... 略 ...
function deleteAction()
{
// 不顯示畫面
$this->getHelper('ViewRenderer')->setNoRender();
$articleTable = new Articles();
// 有找到才刪
if ($articleRow = $articleTable->find(3)->current()) {
$articleRow->delete();
}
}
}
現在瀏覽一下 http://localhost/example3/index/delete (實際位置請按照各位的環境自行輸入) ,畫面應該不會出現任何資訊。現在到資料庫觀察一下,應該會發現 articles 資料表的第 3 筆紀錄已經被刪除;再看看 articles_tags ,在 article_id 為 3 的紀錄也跟著一併被刪除了。這樣就完成串聯刪除啦!
再整理一次重點:
-
在要刪除紀錄的資料表類別裡定義 $_dependentTables 。
-
在要串聯刪除紀錄的資料表類別裡定義 $_referenceMap[關連的名稱] 的 onDelete 屬性。
-
要使用串聯刪除,一定要用 Zend_Db_Row 的 delete 方法。
btw, ZF 的 directory layout 的設計還真的頗頭痛的 Orz
To hermes:
是的,這點在 ZF 的原始碼中可以看到。
至於目錄結構的部份,我會找時間再寫一篇文章說明我的做法。

如果我有:
1.category
2.articles
3.articles_tags
那麼我刪除category,而屬於該category的所有articles都會刪除,但是articles同時關連到articles_tags,而在刪除的時候,articles_tags並沒有被刪除,請教一下解決方式,謝謝。
