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 兩個標籤。現在資料表紀錄如下:

articles 資料表
id category_id title
3 2 jQuery
4 2 Prototype
tags 資料表
id name
3 javascript
4 ajax
articles_tags 資料表
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 的紀錄也跟著一併被刪除了。這樣就完成串聯刪除啦!

再整理一次重點:

  1. 在要刪除紀錄的資料表類別裡定義 $_dependentTables 。

  2. 在要串聯刪除紀錄的資料表類別裡定義 $_referenceMap[關連的名稱] 的 onDelete 屬性。

  3. 要使用串聯刪除,一定要用 Zend_Db_Row 的 delete 方法。



Posted by jaceju at 樂多Roodo! │13:06 │回應(3)Zend Framework
樂多分類:網路/3C 共同主題:PHP 程式設計 工具:編輯本文
Ads by Roodo! 
回應文章
dear jace, 所以說,ZF 在做 cascading delete 時並不是利用 MySQL 內的 reference 囉?
btw, ZF 的 directory layout 的設計還真的頗頭痛的 Orz
Posted by hermes at 2008年02月27日 18:29

To hermes:

是的,這點在 ZF 的原始碼中可以看到。

至於目錄結構的部份,我會找時間再寫一篇文章說明我的做法。
Posted by jaceju at 2008年02月27日 18:34

如果我有:
1.category
2.articles
3.articles_tags
那麼我刪除category,而屬於該category的所有articles都會刪除,但是articles同時關連到articles_tags,而在刪除的時候,articles_tags並沒有被刪除,請教一下解決方式,謝謝。
Posted by fineryplus at 2009年02月9日 23:33