「Zend Framework 日めくり Calendar 2012」の3日目の記事です。何となくQiitaで書いてみます。
Zend Framework でデータベースを扱う時の定番モジュールがZend_Dbです。公式のQuickStartがわかりやすいんですが、TableDataGatewayをDataMapperから使うというガチガチ厳密な使い方をしているので、「難しそう」「面倒くさそう」という印象を受けるかもしれません。
もうちょっとお手軽に、不真面目に使う書き方を考えてみます。解説を長々と書くとダレそうなので2日間に分割します。今日は概要だけ。
用語の整理
Zend_Db
について少し解説。主要なクラスはこの3つです。
-
Zend_Db
… DAL(PDOのWrapper) -
Zend_Db_Table
… O/R Mapper(Table Data Gateway) -
Zend_Db_Table_Row
… O/R Mapper(Row Data Gateway)
Zend_Db
は言ってしまえばPDOのWrapperで、「どんなデータベースでも同じインターフェースで操作できるよ」というだけの代物。素のPDOより対応データベースが少し多くなっています。インターフェースの統一が主目的であり、極薄でオーバーヘッドもあまり無いので、ZFではとりあえず使っておくといいです。単体で使うことも可能です。
Zend_Db_Table
とZend_Db_Table_Row
がZFにおけるO/R Mapperの部分です。O/R Mapperと聞くとActiveRecordを思い浮かべる人が多いと思いますが、ZFの場合はTable Data Gateway / Row Data Gatewayというデザインパターンを採用しています。
参考: http://otndnld.oracle.co.jp/columns/arai-semi/data_access/2/
ActiveRecordより抽象度が一段低く、「テーブルを1つのクラスとみなす」「テーブル単位で操作を行う」「ドメインロジックを持たない」などの特徴を持ちます。ちょっと長くなりそうなのでこの辺の特徴は追々書きます。
テーブル単位の操作が基本になるので、JOINは基本的に行ってくれません。JOINを多用する場合は向いていませんので、Zend_Dbを直接使ったほうがいいです。しかしテーブル単位の操作に限るのであれば、超便利なコンポーネントです。
サンプルソース
githubに置きました。昔作ったパフォーマンス計測用の適当なアプリにブランチを切ってあります。
https://github.com/hirak/performance/tree/DbTable_test
僕が考える、Zend_Dbをカジュアルに使うポイントは4つ。
- DBの初期化はapplication.iniに書く
- スキーマは
Zend_Db_Table_Definition
を利用してConfigファイルにする。テーブルのクラス定義なんて省略しちゃえ - テーブル取得のコードをグローバル関数化して短くする
-
Zend_Db_Table_Row_Abstract
を拡張してActiveRecordにしてしまう。これで拡張ポイントができるので、DataMapperは省略してしまえ
Zend_Db_Tableの書き方のサンプル
もろもろ準備が出来上がった場合、Controllerなどからの呼び出しコードはこんな感じになります。Zend_Db_Table_Row
の拡張がベースになるので、Rowクラスを生成して操作を行います。
<?php
//常にZend_Db_Tableを作成するところから開始します。
$posts = AR('posts');
//テーブルクラスにデータを検索してもらったり
$latestPost = $posts->find(1)->current();
//テーブルクラスに新しいRowデータの雛形を作ってもらったり
$newpost = $posts->createRow();
$newpost->title = '新しいポスト';
$newpost->text = '新しい記事だよー';
$newpost->save();
ActiveRecordだとPosts::find(1)
みたいにstaticメソッドで検索やinsertを済ませるものがありますが、ZFは操作のために必ずZend_Db_Table
のインスタンスが必要です。
検索(SELECT)
主キーでの検索にはZend_Db_Table::find()
を使います。
<?php
//主キーでの検索
list($post) = AR('posts')->find(1);
//もしくは
$post = AR('posts')->find(1)->current();
var_dump($post->toArray());
//$postはそのまま配列風アクセスやオブジェクトアクセスが可能
echo $post->name, "\t", $post['text'], "\n";
キーは複数渡せる仕様なので、戻り値が常にZend_Db_Table_Rowset
になり、配列風になっています。
そのため、先頭の一つしか必要ない場合はlist()を使うか->current()で絞り込む必要があります。個人的にはlistで書くのが好きです。
もうちょっと複雑な検索の場合はZend_Db_Table::fetch()
やZend_Db_Table::fetchRow()
を使います。こちらはwhereやorderが使えます。
<?php
//複雑な検索
// ORDER BY created DESC LIMIT 1 で最新のPOSTを取得する
$latestPost = $posts->fetchRow(null, 'created DESC');
更新(UPDATE)
テーブルクラスからfindしたりcreateRowしたRowオブジェクトは、「生きています」。編集してsave()すると、そのままDBに保存することが可能です。この辺がRow Data Gatewayの特徴ですね。
<?php
list($post) = $posts->find(1);
$post->title = '修正したよ';
$post->text = '修正した記事だよー';
$post->save();
新規作成(INSERT)
テーブルクラスからcreateRowすると何も入っていない空っぽのデータが作成されます。
こいつにデータを突っ込んでsave()するとINSERT文の効果があります。
<?php
$newPost = $posts->createRow();
$newPost->name = '新しい記事だよー';
$newPost->text = '新しい記事だよー';
$newPost->save();
削除(DELETE)
更新と同じく、Rowを取得してからdeleteすることができます。
<?php
list($post) = $posts->find(1);
$post->delete();
後半・解説編に続く