テーブルクラスによる基本的なデータベースアクセス
作成したOffices、Roomsの2つのテーブルクラスを使ってデータベースにアクセスします。
データベースアクセスの準備
前回の記事ではデータベースにアクセスするのにアダプタクラスのオブジェクトを利用しました。テーブルクラスも内部ではアダプタを利用してデータベースにアクセスします。
テーブルクラスを利用する準備として、まずはアダプタオブジェクトを作るソースと設定ファイルを用意します。
<?php require_once 'Zend/Db.php'; require_once 'Zend/Config/Ini.php'; // テーブルクラスのソースを読み込みます // 異なるフォルダ階層から読み込まれてもよいよう、絶対パスで指定します require_once dirname(__FILE__).'/table/Offices.php'; require_once dirname(__FILE__).'/table/Rooms.php'; require_once dirname(__FILE__).'/table/Members.php'; require_once dirname(__FILE__).'/table/Permissions.php'; // 設定を読み込み、アダプタオブジェクトを作ります $config = new Zend_Config_Ini(dirname(__FILE__). '/config/db_info.ini', 'sample_db'); $db = Zend_Db::factory($config->db); // テーブルクラスが使用するアダプタを指定します Zend_Db_Table_Abstract::setDefaultAdapter($db); // MySQLの文字コードを指定します $db->query("set names '" . $config->character_set . "'");
このファイルは他のソースから読み込まれることを想定しています。読み込み元ファイルのフォルダ階層が変わると、テーブルクラスのソースや設定ファイルへの相対パスが変化してしまいます。そのためdirname(__FILE__)
でこのファイルの絶対パスを取得し、そこからの相対パスを指定しています。
テーブルクラスが使用するアダプタオブジェクトを指定する方法は、次の3通りがあります。
方法 | 使用例 |
デフォルトアダプタを指定 | Zend_Db_Table_Abstract::setDefaultAdapter($db); |
コンストラクタで直接指定 | new Offices(array('db' => $db)); |
Zend_Registryから取得 | new Offices(array('db' => 'db_adapter')); |
ここではsetDefaultAdapter
メソッドを使いました。アクセス対象のデータベースが1つのときはこの方法が便利です。
複数のデータベースを使い分ける必要がある場合は、コンストラクタの引数でアダプタオブジェクトを指定します。連想配列に直接アダプタオブジェクトを入れる方法と、Zend_Registryのキーで指定する方法があります。後者はあらかじめZend_Registry::set
メソッドでアダプタオブジェクトを保存しておきます。
今回のサンプルではレコードに日本語が含まれているため、MySQLのset namesコマンドで文字コードを指定しました。使用する文字コードと指定の仕方は、DBMSや実行環境にあわせる必要があります。
このソースで読み込んでいる設定ファイルは次のようになります。
[sample_db] db.adapter = Pdo_Mysql db.params.host = localhost db.params.username = zend db.params.password = zend_pass db.params.dbname = db_zend_sample character_set = sjis
データベースに接続するための設定と、使用する文字コードを記述しています。
参照先から参照元を検索する
それでは、テーブルクラスを使ってデータベースにアクセスしましょう。まずは参照される側のレコードを検索し、さらにそれを参照しているレコードを検索します。ここではOfficesテーブルに入っているオフィスのレコードが参照される側、Roomsテーブルに入っているオフィス内の部屋のレコードが参照している側になります。
<html> <head> <title>参照先から参照元を検索</title> </head> <body> <?php require_once 'table_sample_init.php'; // テーブルクラスのオブジェクトを作ります $office = new Offices(); // 主キーの値が1のレコードを取得します $rows = $office->find(1); echo 'Officesテーブルから' . count($rows) . '行の結果を取得しました。<br/>'; // 結果を1行ずつ参照するためループします foreach ($rows as $row) { // オフィス名を表示します echo $row->office_name . '<br/>'; // 取得したレコードを参照しているRoomsのレコードを取得します $child_rooms = $row->findDependentRowset('Rooms'); foreach($child_rooms as $child_row) { echo '->' . $child_row->room_name . '<br/>'; } } ?> </body> </html>
主キーの値を指定してテーブルを検索するには、テーブルクラスのfind
メソッドを使います。主キーが単一の列か複合キーか、検索対象が1レコードか複数レコードかで引数の指定の仕方が異なるので、表にまとめます。
引数の指定 | 主キー | 検索対象 |
find(col1) | 単一の列 | 1レコード |
find(col1, col2, ...) | 複合キー | 1レコード |
find(array(row1, row2, ...)) | 単一の列 | 複数レコード |
find(array(col1_row1, col1_row2, ...), array(col2_row1, col2_row2, ...), ...) | 複合キー | 複数レコード |
検索対象が1レコードか複数レコードかに関わらず、find
メソッドの戻り値はZend_Db_Table_Rowset
のオブジェクトになります。指定した主キーの値に該当するレコードがテーブルになければ、その分のZend_Db_Table_Row
オブジェクトはZend_Db_Table_Rowset
オブジェクトに含まれません。そのため、Zend_Db_Table_Rowset
オブジェクトに含まれるZend_Db_Table_Row
オブジェクトの数は、find
メソッドの引数で指定した数より少ない可能性があります。
テーブルの列名がそのままテーブルクラスのメンバ名となります。
Zend_Db_Table_Row
オブジェクトのfindDependentRowset
メソッドを呼び出すことで、レコードの参照元を検索することができます。引数には参照元テーブルのテーブルクラス名を指定します。複数のレコードから参照されている可能性があるので、findDependentRowset
メソッドの戻り値はZend_Db_Table_Rowset
オブジェクトになります。
実行結果は下記のようになります。池袋本社と、そこにある部屋のレコードが返されています。
参照元から参照先を検索する
前の例とは逆に、まず参照元のレコードを検索し、そのレコードが参照しているレコードを検索します。
<html> <head> <title>参照元から参照先を検索</title> </head> <body> <?php require_once 'table_sample_init.php'; // テーブルクラスのオブジェクトを作ります $room = new Rooms(); // テーブルの全レコードを取得します $rows = $room->fetchAll(); foreach ($rows as $row) { // 'ref_office'で指定した外部キーで参照している // Officesテーブルのレコードを取得します $parent_office = $row->findParentRow('Offices', 'ref_office'); // 取得した部屋名とオフィス名を表示します echo $row->room_name . '(' . $parent_office->office_name . ')<br/>'; } ?> </body> </html>
テーブルの全レコードを取得するには、テーブルクラスのfetchAll
メソッドを使います。引数でWHERE句、ORDER BY句を指定して絞込みと並べ替えを行うことも可能です。その場合はfetchAll('room_id > 1', 'room_id desc')
というような書式になります。
参照先レコードを検索するには、Zend_Db_Table_Row
オブジェクトのfindParentRow
メソッドを使います。引数は参照先テーブルのテーブルクラス名と、$_referenceMap連想配列のキーです。連想配列のキーを省略すると、第1引数で指定したテーブルクラス名に該当する外部キー情報のうち最初に格納されているものが使われます。
実行結果は次のようになります。部屋のレコードごとに、対応するオフィスのレコードが返されています。
select
メソッドが追加されました。これを使うとZend_Db_Select
オブジェクトを取得することができます。Zend_Db_Select
オブジェクトは前回紹介したように、メソッドの呼び出しでSELECT文を組み立てることができます。テーブルクラスの
fetchAll
メソッドは引数にZend_Db_Select
オブジェクトを渡せるように変更されており、より柔軟な検索が可能になっています。本文中で紹介したようにWHERE句とORDER BY句を引数で渡す方法も利用できます。findDependentRowset
メソッド、findParentRow
メソッドを使うには、まずfind
メソッドやfetchAll
メソッドでZend_Db_Table_Row
オブジェクトを取得する必要があります。そのためfind
メソッド/fetchAll
メソッドでSELECT文を1回実行してから、findDependentRowset
メソッド/findParentRow
メソッドで別のSELECT文を実行して相手側テーブルを検索することになります。Zend_Db_Select
オブジェクトのjoin
メソッドを使えば、SELECT文を1つにまとめることができます。join
メソッドの引数でテーブルの結合条件を指定する必要がありますが、SELECT文の実行回数を減らせるので処理が効率的になります。テーブルクラスでテーブルを更新する
今度はテーブルクラスを使ってテーブルを更新します。これには以下のメソッドが使えます。
操作 | メソッド |
INSERT | insert($value_array) |
UPDATE | update($value_array, $condition) |
DELETE | delete($condition) |
前回の記事で利用したアダプタクラスの更新用メソッドに似ていますが、テーブルクラスのメソッドなのでテーブル名を指定する引数はありません。
テーブルクラスの更新用メソッドは内部でアダプタクラスの更新用メソッドを呼び出しています。getAdapter
メソッドを呼び出せば、テーブルクラスのオブジェクトが使用するアダプタオブジェクトを取得できます。
ここでは例として、UPDATE
メソッドでレコードを更新します。1個のレコードを変更するだけですが、参考のためgetAdapter
メソッドでアダプタオブジェクトを取得し、トランザクション開始と終了の操作を行っています。
<?php require_once 'table_sample_init.php'; $office = new Offices(); // アダプタオブジェクトを取得し、トランザクションを開始します $adapter = $office->getAdapter(); $adapter->beginTransaction(); try{ // 変更するレコードの条件の文字列を作ります $condition = 'office_id = 2'; // 変更後の値を配列に格納します $value_array = array('office_name' => '品川事業所'); // UPDATEを実行します $office->update($value_array, $condition); // コミットします $adapter->commit(); } catch(Exception $e) { // 例外が発生したらロールバックします $adapter->rollback(); }
Officeテーブルの品川分室のレコードのオフィス名を変更しました。ref_parent.phpをもう一度実行し、オフィス名が品川事業所に変わっているのを確認します。