Zend_Db_Table(日本語)注意:このドキュメントでは、英語版のリビジョン 21597, 21829 の更新内容をスキップしています。 導入Zend_Db_Table クラスは、データベースのテーブルへの オブジェクト指向のインターフェイスです。 テーブルに対するさまざまな共通操作のためのメソッドを提供します。 基底クラスは拡張可能なので、独自のロジックを組み込むこともできます。 Zend_Db_Table は、 » テーブルデータゲートウェイ パターンを実装したものです。また、そのほかにも » 行データゲートウェイ パターンを実装したクラスも含んでいます。 Zend_Db_Table を具象クラスとして使用する方法Zend Framework 1.9 以降では、Zend_Db_Table のインスタンスを作成できます。 つまり、一つのテーブルに対して select、insert、update、delete などといった単純な操作を行うためだけにわざわざ基底クラスを継承して設定する必要がなくなるということです。 以下に、もっとも単純な使用例を示します。 Example #1 文字列で名前を指定するだけのテーブルクラスの宣言
これが、もっとも単純な使用例です。 以下で説明する Zend_Db_Table のオプションはまったく設定していません。 具象クラスの使用例に加えてより複雑なリレーション機能を使いたくなったときは Zend_Db_Table_Definition のドキュメントを参照ください。 テーブルクラスの定義データベース内でアクセスしたいテーブルそれぞれについて、 Zend_Db_Table_Abstract を継承したクラスを定義します。 テーブル名およびスキーマの定義そのクラスが定義しているデータベースのテーブルを定義するには、 protected な変数 $_name を使用します。 これは文字列で、データベースでのテーブル名を指定する必要があります。 Example #2 テーブル名を明示的に指定することによるテーブルクラスの宣言
テーブル名を指定しなかった場合のデフォルトは、クラス名となります。 このデフォルトを使用する場合は、クラス名をデータベースでのテーブル名と一致させる必要があります。 Example #3 テーブル名を暗黙的に指定することによるテーブルクラスの宣言
テーブルのスキーマについても、protected 変数 $_schema で宣言できます。 あるいは $_name プロパティでテーブル名の前にスキーマ名をつなげて指定することもできます。 $_name で指定したスキーマのほうが、 $_schema プロパティで指定したスキーマよりも優先されます。 RDBMS によってはスキーマのことを「データベース」や「表領域」 などということもありますが、同じように使用できます。 スキーマを、テーブル名の一部として宣言することもできます。 Example #4 テーブルクラスでのスキーマの宣言
スキーマ名とテーブル名は、コンストラクタの設定ディレクティブでも指定できます。 これは、$_name や $_schema といったプロパティで設定したデフォルト値を上書きします。 name ディレクティブで指定したスキーマ名は、 schema オプションで指定したスキーマ名より優先されます。 Example #5 インスタンス作成時のテーブル名とスキーマ名の指定
スキーマ名を指定しなかった場合のデフォルトは、 そのデータベースアダプタが接続しているスキーマとなります。 テーブルの主キーの定義すべてのテーブルは主キーを持たなければなりません。 主キーカラムを宣言するには、protected 変数 $_primary を使用します。 これは、単一のカラムの名前を表す文字列か、 もし主キーが複合キーの場合はカラム名の配列となります。 Example #6 主キーを指定する例
主キーを指定しなかった場合は、Zend_Db_Table_Abstract は describeTable() メソッドの情報に基づいて主キーを見つけます。
テーブルの設定メソッドのオーバーライドテーブルクラスのインスタンスを作成する際に、 コンストラクタ内でいくつかの protected メソッドをコールします。 これにより、テーブルのメタデータを初期化します。 これらのメソッドを拡張して、メタデータを明示的に定義することも可能です。 その場合は、メソッドの最後で親クラスの同名のメソッドをコールすることを忘れないようにしましょう。 Example #7 _setupTableName() メソッドのオーバーライドの例
オーバーライドできるメソッドは、次のとおりです。
テーブルの初期化テーブルクラスの作成時にアプリケーション固有のロジックを初期化したい場合は、 その作業を init() メソッドで行います。 これは、テーブルのメタデータがすべて処理された後にコールされます。 メタデータを変更するつもりがないのなら、 __construct メソッドよりもこちらを使用することを推奨します。 Example #8 init() メソッドの使用例 テーブルのインスタンスの作成テーブルクラスを使用する前に、コンストラクタでそのインスタンスを作成します。 コンストラクタの引数はオプションの配列となります。 テーブルのコンストラクタのオプションのうち、最も重要なのは データベースアダプタのインスタンスとなります。 これは RDBMS への有効な接続を表します。 データベースアダプタをテーブルクラスに指定する方法は三通りあります。 それぞれについて、以下で説明します。 データベースアダプタの指定データベースアダプタをテーブルクラスに指定する最初の方法は、 Zend_Db_Adapter_Abstract 型のオブジェクトをオプションの配列で渡すことです。 配列のキーは 'db' となります。 Example #9 アダプタオブジェクトを使用した、テーブルの作成の例
デフォルトのデータベースアダプタの設定データベースアダプタをテーブルクラスに指定する二番目の方法は、 デフォルトのデータベースアダプタとして Zend_Db_Adapter_Abstract 型のオブジェクトを宣言することです。そのアプリケーション内で、 これ以降に作成したテーブルインスタンスについてこれが用いられます。 これを行うには、静的メソッド Zend_Db_Table_Abstract::setDefaultAdapter() を使用します。引数は、Zend_Db_Adapter_Abstract 型のオブジェクトとなります。 Example #10 デフォルトアダプタを使用した、テーブルの作成の例
これは、たとえば起動ファイルなどでデータベースアダプタオブジェクトを作成し、 それをデフォルトのアダプタとして保存しておく場合などに便利です。 これにより、アプリケーション全体で共通のアダプタを使用することが保証されます。 しかし、デフォルトのアダプタのインスタンスは、ひとつだけしか設定できません。 データベースアダプタのレジストリへの保存データベースアダプタをテーブルクラスに指定する三番目の方法は、 文字列ををオプションの配列で渡すことです。 配列のキーは、この場合も 'db' となります。 この文字列は、静的な Zend_Registry インスタンスのキーとして使用します。 このキーのエントリが Zend_Db_Adapter_Abstract 型のオブジェクトとなります。 Example #11 レジストリのキーを使用した、テーブルの作成の例
デフォルトアダプタの指定と同様、これにより、 アプリケーション全体で共通のアダプタを使用することが保証されます。 レジストリには複数のアダプタインスタンスを保存できるため、 より柔軟に使用できます。指定したアダプタインスタンスは 特定の RDBMS やデータベースインスタンスに固有のものとなります。 複数のデータベースにアクセスする必要がある場合は、 複数のアダプタが必要です。 テーブルへの行の挿入テーブルオブジェクトを使用して、そのオブジェクトの元になっているテーブルに 行を挿入できます。そのためには、テーブルオブジェクトの insert() メソッドを使用します。引数は連想配列で、 カラム名と値の対応を指定します。 Example #12 テーブルへの挿入の例
デフォルトでは、配列内の値はリテラル値として扱われ、 パラメータを使用して挿入されます。これを SQL の式として扱いたい場合は、 文字列ではない形式で指定する必要があります。その際には Zend_Db_Expr 型のオブジェクトを使用します。 Example #13 式をテーブルに挿入する例
上の例では、テーブルには自動インクリメントの主キーがあるものとします。 これは Zend_Db_Table_Abstract のデフォルトの挙動ですが、 それ以外の形式の主キーも扱えます。以下の節では、 さまざまな形式の主キーを扱う方法を説明します。 自動インクリメントのキーを持つテーブルの使用自動インクリメントの主キーは、SQL の INSERT 文で主キー列を省略した場合に一意な整数値を生成します。 Zend_Db_Table_Abstract で protected 変数 $_sequence の値を boolean の TRUE にすると、そのテーブルは自動インクリメントの主キーを持つものとみなされます。 Example #14 自動インクリメントの主キーを持つテーブルを宣言する例
MySQL、Microsoft SQL Server そして SQLite などの RDBMS が、主キーの自動インクリメントをサポートしています。 PostgreSQL の SERIAL 記法を使用すると、 テーブル名とカラム名をもとにして暗黙的にシーケンスを定義します。 新しい行を作成した際にはこのシーケンスを用いてキーの値を生成します。 IBM DB2 には、これと同等の動作をする IDENTIFY という記法があります。 これらの記法を使用する場合は、Zend_Db_Table クラスで $_sequence を TRUE と設定し、 自動インクリメントを有効にしてください。 シーケンスを持つテーブルの使用シーケンスとはデータベースのオブジェクトの一種で、 一意な値を生成するものです。これを、 ひとつあるいは複数のテーブルの主キーの値として使用できます。 $_sequence に文字列を設定すると、 Zend_Db_Table_Abstract は、それがデータベースの シーケンスオブジェクトの名前であるとみなします。 シーケンスを実行して新しい値を生成し、その値を INSERT 操作で使用します。 Example #15 シーケンスを用いたテーブルを宣言する例
Oracle、PostgreSQL そして IBM DB2 などの RDBMS が、 データベースでのシーケンスオブジェクトをサポートしています。 PostgreSQL および IBM DB2 は、 暗黙的にシーケンスを定義してカラムに関連付ける構文もサポートしています。 この記法を使う場合は、 そのテーブルで自動インクリメントキーのカラムを使用するようにします。 シーケンスのキーの次の値を取得することがある場合にのみ シーケンス名を文字列で定義します。 自然キーを持つテーブルの使用自然キーを持つテーブルもあります。自然キーとは、 テーブルやシーケンスによって自動生成されるもの以外のキーということです。 この場合は、主キーの値を指定する必要があります。 $_sequence の値を boolean の FALSE にすると、Zend_Db_Table_Abstract はそのテーブルが自然キーを持つものとみなします。 insert() メソッドを使用する際には、 主キーカラムの値をデータの配列で指定する必要があります。 指定しなかった場合、このメソッドは Zend_Db_Table_Exception をスローします。 Example #16 自然キーを用いたテーブルを宣言する例
テーブルの行の更新データベースのテーブルの行を更新するには、テーブルクラスの update メソッドを使用します。 このメソッドには二つの引数を指定します。変更するカラムと それらのカラムに代入する新しい値を表す連想配列、 そして UPDATE 操作の対象となる行を指定する WHERE 句で使用する SQL 式です。 Example #17 テーブルの行の更新の例
テーブルの update() メソッドはデータベースアダプタの update() メソッドへのプロキシなので、 二番目の引数は、SQL 式の配列にできます。 その場合、それぞれの式が論理演算子 AND で連結されます。
テーブルからの行の削除データベースのテーブルから行を削除するには、テーブルクラスの delete() メソッドを使用します。 このメソッドにはひとつの引数を指定します。この引数は WHERE 句で使用する SQL 式で、 これにより、削除対象となる行を指定します。 Example #18 テーブルからの行の削除の例
テーブルの delete() メソッドはデータベースアダプタの delete() メソッドへのプロキシなので、 引数は SQL 式の配列とすることもできます。 その場合、それぞれの式が論理演算子 AND で連結されます。
主キーによる行の検索データベースのテーブルに対して、指定した主キーの値に対応する行を問い合わせるには find() メソッドを使用します。 このメソッドの最初の引数は、テーブルの主キーに対応する 単一の値か、あるいは複数の値の配列となります。 Example #19 主キーの値によって行を捜す例
単一の値を指定した場合は、このメソッドが返す行数は最大でも一行になります。 主キーの値が重複することはないので、指定した値に対応する行は テーブル内で最大でも一行だけだからです。 複数の値を配列で指定した場合は、このメソッドが返す結果の最大数は 配列で指定した値の数となります。 find() メソッドの返す行数は、主キーで指定した値より少なくなるかもしれません。 たとえば指定した値に対応する行がデータベースのテーブルに存在しなかった場合などです。 このメソッドが返す行数がゼロになる可能性もあります。 このように結果の行数が可変なので、 find() メソッドが返すオブジェクトの型は Zend_Db_Table_Rowset_Abstract となります。 主キーが複合キーの場合、つまり複数のカラムから構成されるキーの場合は、 追加のカラムを find() メソッドの引数で指定します。 テーブルの主キーのカラム数と同じ数の引数を指定しなければなりません。 複合主キーのテーブルから複数行を取得するには、 各引数を配列で指定します。これらすべての配列の要素数は同じでなければなりません。 各配列の値が、その順にキー列の値として用いられます。 たとえば、すべての配列の最初の要素で複合主キーの最初の値を指定し、 すべての配列の二番目の要素で複合主キーの二番目の値を設定し、…… というようになります。 Example #20 複合主キーの値の指定による行の取得の例 以下の find() メソッドは、データベース内のふたつの行にマッチします。 最初の行の主キーの値は (1234, 'ABC') で、次の行の主キーの値は (5678, 'DEF') となります。
行セットの問い合わせSelect API
Warning
取得操作用の API は変更され、 Zend_Db_Table_Select オブジェクトでクエリを変更できるようになりました。 しかし、昔ながらの方法である fetchRow() や fetchAll() は今でも同じように使用できます。 次の文は、どれも正しくて同じ動作をします。 しかし、新しい使用法に対応するためにもできるだけ新しい書き方に変更することをお勧めします。
Zend_Db_Table_Select オブジェクトは Zend_Db_Select を継承したものであり、 クエリにはいくつか制限があります。追加された機能や制限事項を以下にまとめます。
Example #21 単純な使用法
このコンポーネントでは「流れるようなインターフェイス」 を実装しているので、この例はもっと省略して書くこともできます。
Example #22 流れるようなインターフェイスの例
行セットの取得主キーの値以外を条件として行のセットを問い合わせるには、 テーブルクラスの fetchAll() メソッドを使用します。 このメソッドは、Zend_Db_Table_Rowset_Abstract 型のオブジェクトを返します。 Example #23 式から行を取得する例
ORDER BY での並べ替えの条件句やオフセットを表す整数値を指定して、 クエリの返す結果を絞りこむことができます。 これらの値は LIMIT 句で用いられます。 LIMIT 構文をサポートしていない RDBMS では、それと同等のロジックで用いられます。 Example #24 式を使用した行の検索の例
これらのオプションはどれも、必須ではありません。 ORDER 句を省略した場合は、結果セットに複数の行が含まれる場合の並び順は予測不可能です。 LIMIT 句を省略した場合は、WHERE 句にマッチするすべての行を取得することになります。 高度な使用法リクエストの内容をより明確に指定して最適化するために、 行/行セットが返すカラムの数を絞り込みたいこともあるでしょう。 これは、select オブジェクトの FROM 句で行います。 FROM 句の最初の引数は Zend_Db_Select オブジェクトと同じですが、 さらに Zend_Db_Table_Abstract のインスタンスを渡すこともでき、テーブル名を自動的に検出します。
Example #25 指定したカラムの取得
Important
FROM 句で式を指定すると、その結果を readOnly の行/行セット
として返します。この例では、bugs テーブルを検索して
個人別のバグの報告件数を取得しています。
GROUP 句に注目しましょう。これで、返される行に
'count' というカラムが含まれるようになり、
スキーマの他のカラムと同じようにアクセスできるようになります。
この状態でも、行セット自体は '正しい' 形式です。 単にひとつのテーブルの中の一部のカラムを含んでいるというだけです。 この中の行に対して save() メソッドをコールすると、 そこに含まれているフィールドだけを更新します。
Example #26 式の結果をカラムとして取得する
Example #27 ルックアップテーブルによる fetchAll() の結果の絞込み
Zend_Db_Table_Select の主な使用目的は、 制約を強要して正しい形式の SELECT クエリを作成することです。 しかし時には、Zend_Db_Table_Row の柔軟性が必要であって 行を更新したり削除したりすることはないということもあります。 そんな場合には、setIntegrityCheck に FALSE を渡して行/行セットを取得できます。 この場合に返される行/行セットは 'ロックされた' 行 (save()、delete() やフィールドの設定用メソッドを実行すると例外が発生する) となります。 Example #28 Zend_Db_Table_Select の整合性チェックを削除し、JOIN した行を許可する
単一の行の問い合わせfetchAll() と同じような条件を指定して、 単一の行を問い合わせることができます。 Example #29 式から単一の行を取得する例
このメソッドは、Zend_Db_Table_Row_Abstract 型のオブジェクトを返します。 指定した検索条件に一致する行がデータベースのテーブルにない場合は、 fetchRow() は PHP の NULL 値を返します。 テーブルのメタデータ情報の取得Zend_Db_Table_Abstract クラスは、メタデータに関するいくつかの情報を提供します。 info() メソッドは配列を返し、その中には テーブルについての情報、カラムや主キー、その他のメタデータが含まれます。 Example #30 テーブル名を取得する例
info() メソッドが返す配列のキーについて、 以下にまとめます。
テーブルのメタデータのキャッシュデフォルトでは Zend_Db_Table_Abstract の問合せ先は テーブルオブジェクトのインスタンスの テーブルメタデータ が指すデータベースとなります。 つまり、テーブルオブジェクトを作成する際にデフォルトで行われれることは、アダプタの describeTable() メソッドによってデータベースからテーブルのメタデータを取得するということになります。 これを必要とする操作には次のようなものがあります。
同一のテーブルに対して複数のテーブルオブジェクトを作成する場合などに、 毎回テーブルのめたデータをデータベースに問い合わせることは パフォーマンスの観点からも好ましくありません。 このような場合のために、データベースから取得したテーブルメタデータをキャッシュしておくことができます。 テーブルのメタデータをキャッシュする主な方法は、次のふたつです。
Example #31 すべてのテーブルオブジェクトでのデフォルトのメタデータキャッシュの使用 次のコードは、デフォルトのメタデータキャッシュをすべてのテーブルオブジェクトで使用する方法を示すものです。
Example #32 特定のテーブルオブジェクトでのメタデータキャッシュの使用 次のコードは、メタデータキャッシュを特定のテーブルオブジェクトに設定する方法を示すものです。
上の例では Zend_Cache_Backend_File を使用していますが、 状況に応じて適切なバックエンドを使い分けることができます。詳細な情報は Zend_Cache を参照ください。 テーブルのメタデータのハードコーディングメタデータのキャッシュをより高速にするために、 メタデータをハードコーディングすることもできます。 しかし、そうすると、 テーブルのスキーマが変わるたびにコードを変更しなければならなくなります。 この方法をおすすめできるのは、 実運用環境で最適化が必要となった場合のみです。 メタデータの構造は次のようになります。
適切な値を知るには、メタデータキャッシュを使用するのが簡単でしょう。 キャッシュに格納された値をデシリアライズするのです。 この最適化を無効にするには、 metadataCacheInClass フラグをオフにします。
このフラグはデフォルトで有効になっています。この場合は、 $_metadata 配列はインスタンスの作成時にのみ作成されます。 テーブルクラスのカスタマイズおよび拡張独自の行クラスあるいは行セットクラスの使用デフォルトでは、テーブルクラスが返す行セットは 具象クラス Zend_Db_Table_Rowset のインスタンスであり、 行セットには具象クラス Zend_Db_Table_Row のインスタンスの集合が含まれます。 これらのいずれについても、別のクラスを使用することが可能です。 しかし、使用するクラスはそれぞれ Zend_Db_Table_Rowset_Abstract および Zend_Db_Table_Row_Abstract を継承したものでなければなりません。 行クラスおよび行セットクラスを指定するには、 テーブルのコンストラクタのオプション配列を使用します。 対応するキーは、それぞれ 'rowClass' および 'rowsetClass' となります。 ここには、クラスの名前を文字列で指定します。 Example #33 行クラスおよび行セットクラスの指定の例
クラスを変更するには、 setRowClass() メソッドおよび setRowsetClass() メソッドを使用します。 これは、それ以降に作成される行および行セットに適用されます。 すでに出来上がっている行オブジェクトや行セットオブジェクトには 何の影響も及ぼしません。 Example #34 行クラスおよび行セットクラスの変更の例
行クラスおよび行セットクラスについての詳細は Zend_Db_Table_Row(日本語) および Zend_Db_Table_Rowset(日本語) を参照ください。 Insert、Update および Delete 時の独自ロジックの定義テーブルクラスの insert() メソッドや update() メソッドをオーバーライドできます。 これにより、データベース操作の前に実行される独自のコードを実装できます。 最後に親クラスのメソッドをコールすることを忘れないようにしましょう。 Example #35 タイムスタンプを処理する独自ロジック
delete() メソッドをオーバーライドすることもできます。 Zend_Db_Table における独自の検索メソッドの定義もし特定の条件によるテーブルの検索を頻繁に行うのなら、 独自の検索メソッドをテーブルクラスで実装できます。 大半の問い合わせは fetchAll() を用いて書くことができますが、 アプリケーション内の複数の箇所でクエリを実行する場合には 問い合わせ条件を指定するコードが重複してしまいます。 そんな場合は、テーブルクラスでメソッドを実装し、 よく使う問い合わせを定義しておいたほうが便利です。 Example #36 状況を指定してバグを検索する独自メソッド
Zend_Db_Table における語尾変化の定義テーブルのクラス名を RDBMS のテーブル名とあわせるために、 inflection (語尾変化) と呼ばれる文字列変換を使用することを好む方もいます。 たとえば、テーブルのクラス名が "BugsProducts" だとすると、クラスのプロパティ $_name を明示的に宣言しなかった場合は データベース内の物理的なテーブル "bugs_products" にマッチします。この関連付けでは、 "CamelCase" 形式のクラス名が小文字に変換され、単語の区切りがアンダースコアに変わります。 データベースのテーブル名を、クラス名とは独立したものにすることもできます。 その場合は、テーブルクラスのプロパティ $_name に、そのクラス名を指定します。 Zend_Db_Table_Abstract は、クラス名とテーブル名を関連付けるための語尾変化は行いません。 テーブルクラスで $_name の宣言を省略すると、 そのクラス名に正確に一致する名前のテーブルと関連付けられます。 データベースの識別子を変換することは、適切ではありません。 なぜなら、それは不明確な状態を引き起こし、 時には識別子にアクセスできなくなってしまうからです。 SQL の識別子をデータベース内にあるそのままの形式で扱うことで、 Zend_Db_Table_Abstract はシンプルで柔軟なものになっています。 語尾変化を行いたい場合は、その変換を独自に実装しなければなりません。そのためには テーブルクラスで _setupTableName() メソッドをオーバーライドします。 これを行うひとつの方法としては、Zend_Db_Table_Abstract を継承した抽象クラスを作成し、さらにそれを継承したテーブルクラスを作成するという方法があります。 Example #37 語尾変化を実装した抽象テーブルクラスの例 語尾変化を行う関数を書くのはあなたの役割です。 Zend Framework にはそのような関数はありません。
|