- 2009-06-12 (金) 3:23
- 技術・開発ツール

前回のエントリに基づき、ZF本家のクイックスタートにあるチュートリアルのコードをDoctrineを使って書き直してみた。Zend_Dbを使う場合に一番面倒なモデルのコードの記述が簡単なYAMLファイルを一つ書くだけで済み、コントローラの修正も最低限で済んだ。
新旧の概要を比較を見てみると
オリジナルのディレクトリ構造
Doctrine適用後のディレクトリ構造
- モデルの生成をDoctrinに任せた結果、チュートリアル中の”Create a Model and Database Table”に関わる部分は不要になる。つまりオリジナルの”models/”以下のコードはまるまるDoctrineが生成するコードで置き換わる。
- MySQLを使用するのでdata/に入っていたSQLiteのテーブルを削除。代わりにDoctrineを使用する際に必要になる4つのディレクトリを置く。schema.sqlはDoctrineが生成したものである。
- library/の配下にDoctrine本体(1.1.1)と自分でカスタマイズしたプログラムを入れる。本来はプラグインとして定義すればスマートなのだろうが今回は省略。また環境設定関連の情報はWebアプリとDoctrineのコマンドラインツールの両方から参照される必要があるので、preparation.phpとして外に出しここに配置。
- scripts/の配下はDoctrineのコマンドラインツールを動かすためのdoctrine-cli.phpに置き換える。
- あらかじめZendFrameworkにパスが通っていることが前提。もちろんlibrary/の下にZFを置いてもいい。自分が使用したバージョンは1.8.2。
- 余談だが、library/の下に複数のライブラリを置く場合にどうやって配置するかという問題を感じた。正攻法で行けば各ライブラリごとにサブディレクトリを切ってその中に配置することで明示的に分けるべきだろう。ただしそうなると新たにライブラリを追加するたびにinclude_pathのメンテも行わなければならなくなり面倒だ。一方フラットに配置したとするとlibrary/にさえパスが通っていればあとはAutoloader任せでOKということになる。ただしファイル名の衝突が生じたときにやっかいなことになるだろう。
public/index.phpとscripts/doctrine-cli.phpの両方から呼び出されるpreparation.phpの内容はこんな感じだ。
preparation.php
<?php
// Define path to application directory
defined('APPLICATION_PATH')
|| define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
// Define application environment
defined('APPLICATION_ENV')
|| define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
// Set include_path
set_include_path(implode(PATH_SEPARATOR, array(
realpath(APPLICATION_PATH . '/../library'),
realpath(APPLICATION_PATH . '/../library/doctrine'),
realpath(APPLICATION_PATH . '/models'),
realpath(APPLICATION_PATH . '/models/generated'),
get_include_path()
)));
require_once 'Zend/Loader/Autoloader.php';
$autoloader = Zend_Loader_Autoloader::getInstance();
$autoloader->setFallbackAutoloader(true);
// Create application
$application = new Zend_Application(
APPLICATION_ENV,
APPLICATION_PATH . '/configs/application.ini'
);
$application_ini = new Zend_Config_Ini(APPLICATION_PATH . '/configs/application.ini', null);
Zend_Registry::set('application_ini', $application_ini);
$connection = My_Db_Doctrine::connect($application_ini );
Zend_Registry::set('doctrine_config', array(
'data_fixtures_path' => APPLICATION_PATH . '/../data/fixtures/',
'migrations_path' => APPLICATION_PATH . '/../data/migrations/',
'sql_path' => APPLICATION_PATH . '/../data/sql/',
'yaml_schema_path' => APPLICATION_PATH . '/../data/schema/',
'models_path' => APPLICATION_PATH . '/models/'
));
Doctrineとの接続を行うクラスであるMy_Db_Doctrineも定義する
library/My/Db/Doctrine.php
<?php
class My_Db_Doctrine
{
public static function connect($application_ini){
$data = $application_ini->con_database;
$dsn = sprintf(
'%s://%s:%s@%s/%s',
strtolower($data->type),
$data->username,
$data->password,
$data->host,
$data->dbname
);
$db = Doctrine_Manager::connection($dsn);
return $db;
}
}
今回モデル関連で作成する必要のある唯一のファイルがschema.ymlである。
schema.yml
---
Guestbook:
tableName: guestbook
columns:
id:
primary: true
autoincrement: true
type: integer(10)
email:
type: string(32)
notnull: true
email: true
comment: string(255)
created: timestamp
オリジナルのスキーマにはemailフィールドにデフォルト値を定義しているが今回は省略。また初期データの投入のしかけもスキップした。
今回はSQLiteではなくMySQLを使用するのでapplication.iniファイルに以下のセクションを追記する。
[con_database] name = "db" type = "mysql" dbname = "zfdev" host = "localhost" username = "zfdev" password = "zfdev" charset = "utf8"
scripts/doctrine-cli.phpは実行スクリプトではなくただのphpファイルにした。こうすればWindowsからも使えるはずだ。
doctrine-cli.php
<?php
//Preparation
require_once '../library/preparation.php';
$cli = new Doctrine_Cli(Zend_Registry::get('doctrine_config'));
if (isset($_GET['cmd'])) {
$_SERVER['argv'] = array('doctrine-cli.php', $_GET['cmd']);
}
$cli->run($_SERVER['argv']);
scripts/の中から
php doctrine-cli.php
と実行するとDoctrineのオプション一覧が出てくる。
正しくコマンドラインツールが動いているのが確認できたので今度は
php doctrine-cli.php build-all
を実行。DoctrineがMySQL内にテーブルを作り、同時にapplication/modelsの配下にモデルが生成される。DDL文は別途
php doctrine-cli.php generate-sql
とすることでdata/sqlの配下に生成されるがアプリの動作そのものには必須ではない。
コントローラについて修正が必要なのはapplication/controllers/GuestbookController.phpだけだ。
application/controllers/GuestbookController.php
<?php
class GuestbookController extends Zend_Controller_Action
{
public function init()
{
}
public function indexAction()
{
$guestbook = Doctrine_Query::create()
-> from('Guestbook g')
-> orderBy('g.created DESC')
-> execute();
$this->view->entries = $guestbook;
}
public function signAction()
{
$request = $this->getRequest();
$form = new Default_Form_Guestbook();
if ($this->getRequest()->isPost()) {
if ($form->isValid($request->getPost())) {
$guestbook = new Guestbook();
$guestbook->fromArray($form->getValues(true));
$guestbook->created = new Doctrine_Expression('now()');
$guestbook->save();
return $this->_helper->redirector('index');
}
}
$this->view->form = $form;
}
}
元のコードは、indexAction()が
public function indexAction()
{
$guestbook = new Default_Model_Guestbook();
$this->view->entries = $guestbook->fetchAll();
}
で、signAction()が
public function signAction()
{
$request = $this->getRequest();
$form = new Default_Form_Guestbook();
if ($this->getRequest()->isPost()) {
if ($form->isValid($request->getPost())) {
$model = new Default_Model_Guestbook($form->getValues());
$model->save();
return $this->_helper->redirector('index');
}
}
である。非常に単純なアプリケーションとはいえ拍子抜けするほど簡単に変更できた。(当然ながらビューはまったく変更していない。)
Doctrineを使うと参照整合性制約やモデル同士の一対一/一対多/多対多といった関係、さらにモデル同士の継承でさえも簡単に定義できてしまうという。 もちろんDoctrineという一種のフレームワークを覚えなければならないからその分のコストはかかるしDoctrine独自の制約といった難し さもあるのだろうが、それらを補ってあまりある恩恵が得られると思う。これはトライする価値ありだ。
Comments:0
Trackbacks:1
- Trackback URL for this entry
- http://studiokdf.com/blog/2009/06/137.html/trackback
- Listed below are links to weblogs that reference
- Zend Framework Quick StartにDoctrineを適用してみる from KDF Memo
- pingback from Zend Framework Quick StartにDoctrineを適用してみる - その2 - KDF Memo 09-06-14 (日) 17:58
-
[...] Older [...]


