DB_Table クラスのチュートリアル

DB_Table クラスのチュートリアル --  単一のテーブルへのインターフェイス

説明

このチュートリアルでは、DB_Table クラスを単一のデータベーステーブルへのインターフェイスとして使用する方法を説明します。 このクラスの機能には、次のようなものがあります。

このクラスのチュートリアルは 2 ページで構成されています。 このページでは、DB_Table の全機能のうち HTML_QuickForm のフォーム生成機能以外を取り扱います。 フォーム生成機能については次のページで取り上げます。 このチュートリアルは、もともと別のサイトで公開されていた DB_Table のドキュメントをもとにしています。 このドキュメントの作者は Paul M. Jones で、今でも ここ で見ることができます。元のドキュメントには DB_Table のサブクラスの定義をカスタマイズするサンプルも含まれていますが、 それはここでは扱いません。

DB_Table クラスおよび DB_Table_Database クラスは、どちらも抽象クラス DB_Table_Base を継承しています。 DB_Table_Base から継承したメソッドやプロパティについては DB_Table あるいは DB_Table_Database オブジェクトで同じように使えます。 これらの共有メソッドやプロパティは、それぞれのマニュアルページで取り上げます。

目次

DB_Table の拡張

通常は、DB_Table のインスタンスを直接作成することはありません。 データベース内の各テーブルに対して DB_Table のサブクラスを作成し、そのサブクラスのインスタンスを作成することになります。 テーブルのスキーマは、このオブジェクトのプロパティとして埋め込まれています。 カラムの定義は $col プロパティの配列に格納されており、インデックスは $idx プロパティの配列に格納されています。 通常は、これらのプロパティ配列はサブクラスの定義の中で行います。 これが、テーブルのスキーマ情報を記録する場所となります。

このようなデータベースゲートウェイ、つまり RDBMS の各テーブルにクラスを関連付ける方式の利点のひとつは、 各テーブルに固有のプロパティやふるまいを適切な箇所で定義できるということです。 独自の挙動や検証機能などを作成するには、 既存のクラスメソッドをオーバーライドするか、あるいは新たなメソッドを作成します。 DB_Table には、一般的に使われる SELECT クエリ用に用いる $sql プロパティ配列もあります。

このパッケージは、RDBMS 上のテーブルとそれに対応する DB_Table サブクラスのどちらか一方があれば、 もう一方を自動的に作成することができます。 同じような内容を何度も書く必要はありません。

このチュートリアルでは最初の方法を使用します。 つまり、サブクラスの定義は自分で書くことになります。 もう一方の方式についての説明は DB_Table_Generator クラスのチュートリアルで行います。

カラムの定義

$col プロパティは連想配列で、各キーがカラム名を表します。 キーに対応する値も連想配列で、ここにカラムの定義を指定します。 カラム定義の中の必須要素 'type' の値は DB_Tableデータ型 の名前である必要があります。たとえば 'integer'、'decimal'、'boolean' などです。 文字列型である 'char' および 'varchar'、数値型である 'decimal' には、 さらに 'size' 要素も必須となります。これは整数値で、文字数あるいは桁数を指定します。 また、'decimal' 型の場合は 'scope' 要素も必須です。 これは、小数点以下の桁数を表します。'require' 要素に true を指定すると、SQL における NOT NULL と同じ意味になり、 このカラムには NULL 値を格納できなくなります。

例として、GuestBook テーブルを作成することを考えてみましょう。 このテーブルには、訪問者の姓と名、メールアドレス、 そして書き込み日時を記録するものとします。さらに、 各行に一意な ID を与える必要があります。 このテーブルのカラムは次のようになるでしょう。

これらのカラム定義はプロパティ配列 $col で行う必要があります。 以下に、GuestBook_Table という名前の DB_Table サブクラスにおける $col の宣言の例を示します。これが、データベースの GuestBook テーブルに対応するものとなります。

インデックスの定義

プロパティ配列 $idx を使用してインデックスを宣言します。個々のインデックスは 'primary'、'unique' あるいは 'normal' のいずれかの型で宣言します。 primary や unique を指定した場合、そのキーの値はテーブル内で一意でなければならなくなります。 normal は、単に効率を上げるためだけのインデックスです。 単一カラムのインデックスおよび複数カラムのインデックスのどちらでも定義できます。

今回の例の GuestBook では、単一カラムのインデックスをふたつ作成します。まず最初は "id" カラムに対する主キーインデックスです。もうひとつは、"signdate" カラムに通常のインデックスを作成します。訪問者を日付や時刻で検索することがあると想定されるからです。 この場合の $idx プロパティの宣言は以下のようになります。

$idx 配列における各要素のキーがインデックス名となります。 対応する値は一般的には配列となり、そのインデックスの定義がここに格納されます。 インデックスの定義配列には 'type' と 'cols' の 2 つの要素が必ず存在します。 'type' の値は 'primary'、'unique' あるいは 'normal' のいずれかとなります。 'cols' 要素の値は、単一のカラム名か単一カラム名の配列、 あるいは複数のカラム名を含む配列のいずれかとなります。 単一カラムのインデックスについては別の省略形もありますが、 それはあとで説明します。

複数カラムのインデックスの例として、fname と lname カラムに対するインデックス "namefl" を定義してみます。

単一カラムのインデックスをお手軽に宣言するための方法も準備されています。 カラム名と同じ名前で単一カラムのインデックスを作成したい場合は、 カラム名を $idx のキーにしてそのインデックスの型を表す文字列 (定義配列ではありません) を値にします。

自動インクリメントのカラムの宣言

各テーブルについてひとつの整数型カラムを、 自動インクリメントのカラムとして宣言することができます。 このカラムは、通常は主キーとなります。この宣言をすることによって変化するのは、 insert() メソッドの振る舞いだけです。 insert() メソッドを実行する際に 自動インクリメントのカラムを省略したり NULL を渡したりすると、 自動生成された整数値が挿入されます。

あるカラムを自動インクリメントとして設定するには、 $auto_inc_col プロパティの値にそのカラムの名前を指定します。次の例では、 GuestBook テーブルの主キーカラムである "id" を自動インクリメントに設定します。

DB_Table のコンストラクタ

個々の DB_Table オブジェクトは、DB あるいは MDB2 のデータベース接続オブジェクトをラップしています。 これらのオブジェクトは、DB_Table のコンストラクタへの最初のパラメータとして渡します。 ひとつの DB/MDB2 オブジェクトを、 複数の DB_Table オブジェクトで共用することができます。 コンストラクタの 2 番目のパラメータには、 関連付ける RDBMS のテーブル名を指定します。 オプションの 3 番目のパラメータ $create を指定すると、 コンストラクタの実行時にテーブルが存在しなければテーブルを作成します。 テーブルが存在する場合はその構造を検証します (詳しくは後ほど説明します)。

以下の例では、GuestBook サブクラスのインスタンスを作成しています。 このクラスは GuestBook テーブルをバインドします。 $auto_create = 'safe' を指定することで、 GuestBook という名前のテーブルが存在しない場合に作成させるようにしています。 テーブルが存在する場合は何もしません。
接続オブジェクト $conn は DB あるいは MDB2 オブジェクトであり、参照で渡します。

$create に指定できる値は以下のとおりです。

データの変更: 追加、更新そして削除

DB_Table のメソッド insert()update() および delete() は、データの行を追加したり更新したり削除したりするための便利なインターフェイスです。 行を追加したり更新したりする場合は、insert メソッドや update メソッドに連想配列を渡します。この配列のキーがカラム名となります。

行の追加

GuestBook テーブルに行を追加するには、 insert() メソッドを使用します。 このメソッドに渡すパラメータは連想配列で、 そのキーがカラム名、そして追加する値をキーに関連づけます。

デフォルトでは、insert メソッドは自動的にデータ型の検証を行います。 また、自動インクリメントカラム以外の必須カラムについてのデータが存在するかどうかも調べます。 これらの検証に失敗した場合は、このメソッドは PEAR Error を返し、データの追加は行いません。 上の例では必須カラム 'id' の値を指定していませんが、 これは 'id' カラムが自動インクリメント型だからです。 詳しくは以下で説明します。

自動インクリメントカラムおよびシーケンス

DB_Table は、 DB あるいは MDB2 が作成したシーケンスを使用して自動インクリメントの整数型の ID を生成することができます。insert メソッドで 自動インクリメント型のカラムの値が設定されていなかったり PHP の NULL が設定されていたりした場合は、 自動インクリメントのシーケンスの値を使用してそのカラムにデータを追加します。 上の例では、'id' カラムに対して、 シーケンスの値を取得して追加します。 しかし、insert メソッドで追加する値が指定されていた場合はそちらを使用します。

DBMDB2 のシーケンス生成機能に直接アクセスするには DB_Table::nextID() メソッドを使用します。 このメソッドは単なるラッパーで、内部では DB あるいは MDB2 の対応するメソッドをコールしています。 次の例では、nextID() を使用して自動インクリメントの値を生成し、 その値を追加するようにしています。
'id' カラムは自動インクリメントとして宣言されているので、 このコードと先ほどの例のコードとはまったく同じ動作をします。

行の更新

update() メソッドは、 単一の行あるいは複数行のセットのデータを用いた更新を行います。 このメソッドのパラメータは 2 つです。最初のパラメータは連装配列で、 更新するカラム名がキー、更新する値が連装配列の値となります。 2 番目の引数は、SQL の UPDATE 文で使用する WHERE 句を表す文字列です。 ただし WHERE キーワードそのものは含めません。

たとえば、すべての行で姓を "Smith" から "Jones" に変更するには次のようにします。

insert の場合と同様、カラムの宣言の際に指定した型と違う型の値で更新しようとすると update は失敗します。この場合は PEAR Error を返します。

行の削除

delete() メソッドは、 条件を満たす行あるいは行のセットを削除します。 唯一のパラメータは、削除する行を表す WHERE 条件の文字列です。 ただし WHERE キーワード自体はのぞきます。

たとえば、昨日以前に入力された行をすべて削除するには次のようにします。

データの取得

DB_Table は、配列形式を使用して SQL の SELECT 文を構築します。SELECT 文の各句 (たとえば SELECT、 FROM、WHERE 句など) がそれぞれ個別の要素となります。 これにより、クエリの変更が簡単にできるようになります。 たとえば返される結果の行数を制限するために WHERE 句を追加するといったことも簡単です。 クエリの内容を表す配列は、プロパティ配列 $sql に格納されるようになります。

クエリをデータベースに送信する方法は二通りあります。 select() メソッドは結果セットを配列で返し、 selectResult() メソッドは結果セットを DB_Result あるいは MDB2_Result_Common のオブジェクトで返します。 これらのメソッドはすべて DB_Table_Base 基底クラスから継承したものであり、DB_Table_Database オブジェクトのメソッドとしても使用可能です。

クエリ配列

SQL の select 文は "クエリ配列" として定義されます。 クエリ配列で使用できる要素のキーのほとんどは、 SELECT 文の句に使用するキーワードを小文字にしたものとなります (たとえば 'select'、'from'、'where' など)。 また、それに対応する値はその句の残りの部分 (キーワード以外の部分) となります。クエリ配列のキーに使用できる SELECT コマンドの句は次のとおりです。

'select'、'from'、'where'、'group'、'having' そして 'order' の各要素の値は、SQL 文のそれぞれの句に対応する文字列となります。 ただし、それぞれのキーワード SELECT、FROM、WHERE、GROUP BY、HAVING そして ORDER はのぞきます。もし 'join' 要素が存在した場合、 その値を単純に 'FROM' 要素の後に続けます。つまり、ここでは 'JOIN'、'INNER JOIN' あるいは 'LEFT JOIN' といったキーワードも含める必要があります。

クエリ配列で使用できるその他のキーは、select() メソッドからの返り値をどのような形式にするかを指定するものです。 たとえば以下のようなものがあります。

これらの 3 つの要素が影響するのは select() メソッドだけであり、 selectResult() メソッドの結果には何の影響も及ぼしません。 'get' 要素で使用できる値は次のとおりです。

'get' 要素に使用できる値は、それぞれ DB および MDB2 の get* メソッドの名前と対応します。これらのメソッドが、 DB_Table::select() の内部でコールされます。つまり、 'get' 要素の値が 'all' の場合はクエリを発行する際に DB/MDB2::getAll() メソッドをコールするということです。 この際に、クエリ配列の内容をもとにして SQL クエリを作成します。そして DB_Table::select()getAll() の返り値をそのまま返します。同様に 'get' 要素の値が 'assoc' の場合は getAssoc()、 'col' の場合は getCol()、 'one' の場合は getOne()、そして 'row' の場合は getRow() をコールします。

'get' 要素を 'all' とすると (デフォルトでこのようになります)、 DB_Table::select() メソッドが返す配列の各行は カラム名をキーとする連想配列か数値添字配列、 あるいはカラム名をプロパティに対応させたオブジェクトのいずれかとなります。 返される行のデータ構造を決めるのは、クエリの 'fetchmode' 要素です。もしこれが設定されていない場合は、 DB_Table オブジェクトの $fetchmode プロパティを使用します。同様に、行をオブジェクトとして返す場合に そのオブジェクトのクラス名を決めるのはクエリの 'fetchmode_object_class' 要素です。これが設定されていない場合は DB_Table オブジェクトの $fetchmode_object_class プロパティを使用します。

クエリの 'fetchmode' 要素や 'fetchmode_object_class' 要素、 あるいは DB_Table のプロパティ $fetchmode や $fetchmode_object_class を使用すると、 内部で使用している DBMDB2 オブジェクトの 'fetchmode' プロパティや 'fetchmode_object_class' プロパティ (DB) あるいは 'fetchmode' オプションや 'fetchmode_object_class' オプション (MDB2) を一時的にリセットすることができます。 指定する値は、それぞれ DB_FETCHMODE_* 定数や MDB2_FETCHMODE_* 定数の値でなければなりません。 もともと DB/MDB2 オブジェクトのプロパティやオプションに設定されていた値は、 select() メソッドが値を返す前に復元されます。

クエリの保存: $sql プロパティ配列

よく使われるクエリや、より複雑なクエリを作成する際の元となる "ベースライン" クエリなどは、パブリックプロパティ配列 $sql に格納しておくとよいでしょう。$sql プロパティは連想配列で、 クエリの名前が連想配列のキー、クエリの配列が連想配列の値となります。 保存されたクエリを実行するには、そのクエリに対応するキーの名前を select*() メソッドの引数として指定します。

$sql プロパティは DB_Table_Base クラスから継承したものです。したがって、クエリを DB_Table の $sql プロパティに保存して特定のテーブルへのインターフェイスとすることもできますし、 DB_Table_Database オブジェクトの $sql プロパティに保存してデータベース全体で使用することもできます。 ひとつのテーブルしか使用しないアプリケーションやクエリでは、 DB_Table オブジェクトの $sql プロパティを上の例のように使うと便利です。そのオブジェクトの select*() メソッドで、名前を指定してクエリを実行することができます。 しかし、より複雑なアプリケーションやクエリなどでは、すべてのクエリを DB_Table_Database オブジェクトの $sql プロパティに格納するほうが便利です。そして、このオブジェクトの select*() メソッドでクエリを発行するようにします。

クエリの発行: select*() メソッド

select() メソッドは、 クエリを発行してその結果セットを配列で返します。 selectResult() メソッドは、 結果を DB_Result/MDB2_Result_Common オブジェクトで返します。 selectCount() メソッドは、 クエリが返す結果の行数を整数値で返します。 実際の結果セットは返しません。

これらのメソッドのインターフェイスは、どれも同じです。 まず最初のパラメータは必須で、ここにはクエリ配列かあるいは $sql プロパティ配列のキーを指定します。キーを指定した場合は、 それに対応するクエリ配列を使用するようになります。 残りのパラメータはすべてオプションで、 クエリをデータベースに発行する前に手を加えるためのものです。 2 番目のパラメータ $filter は、SQL の論理式を含む文字列で、 SELECT コマンドの WHERE 句に AND で連結されます。 3 番目のパラメータ $order を指定した場合は、 それを用いて 'ORDER BY' 句を作成します。この場合、クエリの 'order' 要素の内容は無視されます。 4 番目と 5 番目のパラメータである $start と $count には整数値を指定し、結果セットから返す最初の行の位置と最大の行数を指定します。

次の例は、保存されているクエリを filter、 order、start および count パラメータで修正するものです。

select() で結果の行を連想配列で返すようにするには、 クエリ配列の 'fetchmode' 要素か DB_Table オブジェクトの $fetchmode プロパティを変更して DB_FETCHMODE_ASSOC か MDB2_FETCHMODE_ASSOC の適切なほうを指定しなければなりません。

select() で、 カラム名に対応するプロパティを持つオブジェクトで結果の行を返すようにするには、 クエリ配列の 'fetchmode' 要素か DB_Table オブジェクトの $fetchmode プロパティを変更して DB_FETCHMODE_OBJECT か MDB2_FETCHMODE_OBJECT の適切なほうを指定しなければなりません。 ユーザ定義のクラスが指定されなかった場合は、 次の例のようにすべての行が stdClass のインスタンスとして返されます。

結果セットの行をカプセル化するクラスを指定するには、クエリ配列の 'fetchmode_object_class' 要素あるいは $fetchmode_object_class プロパティでクラス名を指定します。

データ型の検証

DB_Table は、自動的にデータの検証を行うことができ、 追加したり更新したりするデータが対応するカラムの型と整合性があるかどうかを調べることができます。 データ型の検証は、追加や更新の際にデフォルトで行われます。 追加や更新の際の検証を有効にしたり無効にしたりするには、それぞれ autoValidInsert() メソッドおよび autoValidUpdate() メソッドを使用します。 これらのメソッドには boolean 型のパラメータを指定し、 true の場合は検証機能が有効に、false の場合は無効にします。

追加したり更新したりするデータの型の検証を実施亜に行うのは、それぞれ validInsert() メソッドあるいは validUpdate() メソッドとなります。 これらのメソッドにはひとつのパラメータを指定します。 このパラメータに渡すのは、カラム名がキーとなる連想配列です。 検証に成功した場合は true、失敗した場合は PEAR_Error オブジェクトを返します。 これらのメソッドは、自動検証を有効にしている場合に insert() メソッドや update() メソッドの内部から自動的にコールされます。 自動検証機能をカスタマイズするには、これらのメソッドをオーバーライドして実装します。

単一のカラムの型を検証するのが isValid() メソッドです。 このメソッドの最初のパラメータには、検証したい値を指定します。 そして 2 番目のパラメータには、検証したい DB_Table データ型の名前を指定します。 このメソッドは、行の検証用の 2 つのメソッドの内部からコールされます。

さまざまなユーティリティメソッド

表 39-1さまざまなメソッド

メソッド説明
quote()SQL クエリで使用するために、 適切な方式で値のクォートとエスケープを行います。 DB::smartQuote()MDB2::quote() メソッドの単純なラッパーで、 内部ではこれらのメソッドをコールしています。
recast()データの連想配列 (カラム名が連想配列のキー、 カラムの値が連想配列の値) を受け取り、 個々の値をそのカラム用の適切なフォーマットにキャストします。
getBlankRow()カラム名をキーとし、各カラムの空の値を関連づけた連想配列を返します。 連想配列の値は、関連づけられたいるカラムの型に応じた適切なフォーマットの値となります。