SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Zend Framework入門

Zend Framework入門(7):抽象化レイヤによるデータベースアクセス手法 - Zend_Db(後編) -

Zend Frameworkによる実践的なPHPアプリケーション開発 7

  • X ポスト
  • このエントリーをはてなブックマークに追加

テーブルクラスによる基本的なデータベースアクセス

 作成したOffices、Roomsの2つのテーブルクラスを使ってデータベースにアクセスします。

データベースアクセスの準備

 前回の記事ではデータベースにアクセスするのにアダプタクラスのオブジェクトを利用しました。テーブルクラスも内部ではアダプタを利用してデータベースにアクセスします。

 テーブルクラスを利用する準備として、まずはアダプタオブジェクトを作るソースと設定ファイルを用意します。

table_sample_init.php
<?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や実行環境にあわせる必要があります。

 このソースで読み込んでいる設定ファイルは次のようになります。

config/db_info.ini
[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テーブルに入っているオフィス内の部屋のレコードが参照している側になります。

ref_child.php
<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メソッドの引数
引数の指定主キー検索対象
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オブジェクトになります。

 実行結果は下記のようになります。池袋本社と、そこにある部屋のレコードが返されています。

参照先から参照元を検索した結果
参照先から参照元を検索した結果

参照元から参照先を検索する

 前の例とは逆に、まず参照元のレコードを検索し、そのレコードが参照しているレコードを検索します。

ref_parent.php
<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引数で指定したテーブルクラス名に該当する外部キー情報のうち最初に格納されているものが使われます。

 実行結果は次のようになります。部屋のレコードごとに、対応するオフィスのレコードが返されています。

参照元から参照先を検索した結果
参照元から参照先を検索した結果
Zend Frameworkの新バージョンで追加された検索
 Zend Framework 1.5ではテーブルクラスに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文の実行回数を減らせるので処理が効率的になります。

テーブルクラスでテーブルを更新する

 今度はテーブルクラスを使ってテーブルを更新します。これには以下のメソッドが使えます。

テーブルクラスのテーブル更新用メソッド
操作メソッド
INSERTinsert($value_array)
UPDATEupdate($value_array, $condition)
DELETEdelete($condition)

 前回の記事で利用したアダプタクラスの更新用メソッドに似ていますが、テーブルクラスのメソッドなのでテーブル名を指定する引数はありません。

 テーブルクラスの更新用メソッドは内部でアダプタクラスの更新用メソッドを呼び出しています。getAdapterメソッドを呼び出せば、テーブルクラスのオブジェクトが使用するアダプタオブジェクトを取得できます。

 ここでは例として、UPDATEメソッドでレコードを更新します。1個のレコードを変更するだけですが、参考のためgetAdapterメソッドでアダプタオブジェクトを取得し、トランザクション開始と終了の操作を行っています。

table_update.php
<?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をもう一度実行し、オフィス名が品川事業所に変わっているのを確認します。

変更結果の確認
変更結果の確認

次のページ
テーブルクラスにメソッドを追加する

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
Zend Framework入門連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 川北 季(カワキタ ミノル)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/2536 2008/06/12 14:00

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング