導入注意:このドキュメントでは、英語版のリビジョン 21587 の更新内容をスキップしています。 導入リレーショナルデータベースでは、テーブル間の関連 (リレーション) が設定されています。 あるテーブル内のエンティティが、 データベーススキーマで定義されている参照整合性制約を使用して 他のエンティティとリンクしているのです。 Zend_Db_Table_Row クラスは、他のテーブルの 関連する行を問い合わせるためのメソッドを持っています。 リレーションの定義抽象クラス Zend_Db_Table_Abstract を継承して、各テーブル用のクラスを作成します。 詳細は テーブルクラスの定義 を参照ください。 また、以下のコードで使用しているデータベースの構成については サンプルデータベース を参照ください。 以下に、これらのテーブルに対応する PHP クラス定義を示します。
Zend_Db_Table で UPDATE や DELETE の連鎖操作をエミュレートする場合は、 配列 $_dependentTables を親テーブルで宣言し、 従属しているテーブルをそこで指定します。 SQL でのテーブル名ではなく、クラス名を使用するようにしましょう。
各従属テーブルのクラス内で、配列 $_referenceMap を宣言します。これは、参照の "ルール" を定義する連想配列となります。 参照ルールとは、リレーションの親テーブルが何になるのか、 従属テーブルのどのカラムと親テーブルのどのカラムが対応するのかを示すものです。 ルールのキーを、配列 $_referenceMap のインデックスとして使用します。 このルールのキーは、各リレーションを指定する際に使用します。 わかりやすい名前をつけるようにしましょう。 あとでご覧いただくように、PHP のメソッド名の一部を使用するとよいでしょう。
上のサンプル PHP コードでは、Bugs テーブルクラスのルールのキーは
配列 $_referenceMap の各ルールエントリの内容もまた、連想配列です。 このルールエントリの内容について、以下で説明します。
従属行セットの取得親テーブルに対するクエリの結果を Row オブジェクトとして取得すれば、 その行を参照している従属テーブルの行を取得できます。 使用するメソッドは、次のようになります。 このメソッドは Zend_Db_Table_Rowset_Abstract オブジェクトを返します。 その中には、従属テーブル $table の行のうち、$row が指す行を参照しているものが含まれます。 最初の引数 $table には、 従属テーブルのクラス名を表す文字列を指定します。 文字列ではなく、テーブルクラスのオブジェクトで指定することもできます。 Example #1 従属行セットの取得
この例では、
二番目の引数 $rule はオプションです。 これは、従属テーブルクラスの配列 $_referenceMap でのルールのキーの名前を指定します。 ルールを指定しなかった場合は、配列の中で その親テーブルを参照している最初のルールを使用します。 最初のもの以外のルールを使用する必要がある場合は、 キーを指定しなければなりません。
上の例のコードでは、ルールのキーを指定していません。
したがって、親テーブルにマッチする最初のルールをデフォルトで使用します。
ここでは Example #2 ルールを指定することによる従属行セットの取得
この例では、
条件や並び順の指定、行数の制限を追加するには、 親の行の select オブジェクトを使用します。
Example #3 Zend_Db_Table_Select による従属行セットの取得
この例では
上のパターンにおいて、
Example #4 マジックメソッドの使用による従属行セットの取得 この例では、先ほどの例と同じ従属行セットを見つける方法を示します。 今回は、テーブルとルールを文字列で指定するのではなく、 マジックメソッドを使用します。
親の行の取得従属テーブルに対するクエリの結果を Row オブジェクトとして取得すれば、 その従属行が参照している親テーブルの行を取得できます。 使用するメソッドは、次のようになります。
従属テーブルに対応する親テーブルの行は、常にひとつだけです。 したがって、このメソッドは Rowset オブジェクトではなく Row オブジェクトを返します。 最初の引数 $table には、 親テーブルのクラス名を表す文字列を指定します。 文字列ではなく、テーブルクラスのオブジェクトで指定することもできます。 Example #5 親の行の取得
この例では、
二番目の引数 $rule はオプションです。 これは、従属テーブルクラスの配列 $_referenceMap でのルールのキーの名前を指定します。 ルールを指定しなかった場合は、配列の中で その親テーブルを参照している最初のルールを使用します。 最初のもの以外のルールを使用する必要がある場合は、 キーを指定しなければなりません。
上の例のコードでは、ルールのキーを指定していません。
したがって、親テーブルにマッチする最初のルールをデフォルトで使用します。
ここでは Example #6 ルールを指定することによる親の行の取得
この例では、テーブル
別の方法として、"マジックメソッド" を使用して親テーブルの行を問い合わせることもできます。 以下のパターンのいずれかに該当するメソッドを Row オブジェクトでコールすると、 Zend_Db_Table_Row_Abstract は findParentRow('<TableClass>', '<Rule>') メソッドを実行します。
上のパターンにおいて、
Example #7 マジックメソッドの使用による親の行の取得 この例では、先ほどの例と同じ親の行を見つける方法を示します。 今回は、テーブルとルールを文字列で指定するのではなく、 マジックメソッドを使用します。
多対多のリレーションを使用した行セットの取得多対多のリレーションの片方のテーブル (この例では "元テーブル" と呼ぶことにします) に対するクエリの結果を Row オブジェクトとして取得すれば、もう一方のテーブル (この例では "対象テーブル" と呼ぶことにします) の対応する行を取得できます。 使用するメソッドは、次のようになります。 このメソッドは Zend_Db_Table_Rowset_Abstract オブジェクトを返します。 その中には、テーブル $table の行のうち、多対多のリレーションを満たすものが含まれます。 元テーブルの行 $row を使用して中間テーブルの行を探し、 さらにそれを対象テーブルと結合します。 最初の引数 $table には、 多対多のリレーションの対象テーブルのクラス名を表す文字列を指定します。 文字列ではなく、テーブルクラスのオブジェクトで指定することもできます。 二番目の引数 $intersectionTable には、 多対多のリレーションの中間テーブルのクラス名を表す文字列を指定します。 文字列ではなく、テーブルクラスのオブジェクトで指定することもできます。 Example #8 多対多の形式の行セットの取得
この例では、元テーブル
三番目と四番目の引数 $rule1 および $rule2 はオプションです。 これは、中間テーブルの配列 $_referenceMap でのルールのキーの名前を表す文字列です。
$rule1 は、中間テーブルから元テーブルへのリレーションを表す
ルールのキーです。この例では、
$rule2 は、中間テーブルから対象テーブルへのリレーションを表す
ルールのキーです。この例では、 親や従属行を取得するメソッドと同様、もしルールを指定しなければ、 配列 $_referenceMap の中でそのリレーションに該当する最初のルールを使用します。 最初のもの以外のルールを使用する必要がある場合は、 キーを指定しなければなりません。
上の例のコードでは、ルールのキーを指定していません。
したがって、マッチする最初のルールをデフォルトで使用します。
ここでは、$rule1 が Example #9 ルールを指定することによる多対多の形式の行セットの取得
この例では、元テーブル
別の方法として、"マジックメソッド"
を使用して多対多のリレーションの対象テーブルの行を問い合わせることもできます。
以下のパターンのいずれかに該当するメソッドをコールすると、
Zend_Db_Table_Row_Abstract は
上のパターンにおいて、
Example #10 マジックメソッドの使用による多対多の形式の行セットの取得 この例では、製品からの多対多のリレーションの 対象テーブルの行を見つける方法を示します。 そのバグに関連する製品を見つけます。
書き込み操作の連鎖
親テーブルに対して UPDATE あるいは DELETE を行った際に、 従属テーブルに対して行う操作を指定できます。 Example #11 連鎖削除の例
この例では
同様に、UPDATE で親テーブルの主キーの値を変更した場合は、 従属テーブルの外部キーの値も自動的に新しい値に更新したくなることでしょう。 これにより、その参照を最新の状態にできます。 シーケンスなどの機能を用いて主キーを生成している場合は、 通常はその値を変更する必要はありません。しかし、 自然キー を使用している場合は、 値が変わる可能性もあります。そのような場合は、 従属テーブルに対して連鎖更新を行う必要があるでしょう。 Zend_Db_Table で連鎖リレーションを宣言するには、 $_referenceMap の中でのルールを編集します。 連想配列のキー 'onDelete' および 'onUpdate' をそれらのオプションの一つに設定します。
親テーブルから行が削除されたり、主キーの値が更新されたりする前に、 その親の行を参照する従属テーブルの行が最初に削除あるいは更新されます。 Example #12 連鎖操作の宣言の例
以下の例では、
以下の例では、親クラスの主キーの値が変更されても
連鎖更新は起こりません。これは、参照マップのエントリの要素
連鎖操作に関する注意点Zend_Db_Table が実行する連鎖操作はアトミックではありません。 つまり、もしデータベース自身が参照整合性制約を実装している場合、 Zend_Db_Table クラスが実行した連鎖 UPDATE がその制約と競合し、参照整合性に違反してしまうことになるということです。 Zend_Db_Table の連鎖 UPDATE を使用できるのは、 データベース側で参照整合性制約を設定していない場合 のみ です。 連鎖 DELETE に関しては、参照整合性に違反してしまう恐れはあまりありません。 従属行の削除は、参照する親の行が削除される前に アトミックでない処理として行うことができます。 しかしながら、UPDATE および DELETE のどちらについても、アトミックでない方法でデータを変更すると、 整合性がない状態のデータを他のユーザに見られてしまうというリスクが発生します。 たとえば、ある行とそのすべての従属行を削除することを考えましょう。 ほんの一瞬ですが、「従属行は削除したけれど親行はまだ削除していない」 という状態を他のクライアントプログラムから見られてしまう可能性があります。 そのクライアントプログラムは、従属行がない親行を見て、 それが意図した状態であると考えることでしょう。 クライアントが読み込んだデータが 変更の途中の中途半端な状態であることなど、知るすべもありません。 アトミックでない変更による問題を軽減するには、 トランザクションを使用してその変更を他と隔離します。 しかし RDBMS によってはトランザクションをサポートしていないものもありますし、 まだコミットされていない "ダーティな" 変更を他のクライアントから見られるようにしているものもあります。 Zend_Db_Table の連鎖処理は Zend_Db_Table からのみ実行できます。 Zend_Db_Table クラスで定義した連鎖削除や更新は、Row クラスで save() メソッドあるいは delete() メソッドを実行した際に適用されます。 しかし、クエリツールや別のアプリケーションなどの 別ルートでデータを更新あるいは削除した場合は、 連鎖操作は発生しません。Zend_Db_Adapter クラスの update() メソッドや delete() メソッドを実行したとしても、Zend_Db_Table で定義した連鎖操作は実行されません。 連鎖 INSERT はありません。 連鎖 INSERT はサポートしていません。 親テーブルに行を追加したら、 従属テーブルへの行の追加は別の処理として行う必要があります。
|