This project is no longer maintained and has been archived. |
はじめに
最も低いレベルで、DoctrineはPHPクラスの一式でデータベーススキーマを表現します。これらのクラスはスキーマとモデルの振る舞いを定義します。
ウェブアプリケーションのユーザーを表す基本モデルは次のようになります。
class User extends Doctrine_Record { public function
setTableDefinition() { $this->hasColumn('username', 'string', 255); $this->hasColumn('password', 'string', 255); }
public function setUp()
{
$this->actAs('Timestampable');
}
}
NOTE 実際には上記のクラスは使いません。これらは単なる例です。この章の後の方で既存のデータベーステーブルから最初のクラスの定義を生成します。
Doctrine\_Record
のそれぞれの子クラスはsetTableDefinition()
とsetUp()
メソッドを持ちます。setTableDefinition()
メソッドはカラム、インデックスとテーブルのスキーマに関するその他の情報を定義するためにあります。setUp()
メソッドはビヘイビアとDoctrine_Record
子クラスの間のリレーションを定義するためにあります。上記の例ではautomagic機能を追加するTimestampableビヘイビアを有効にしています。[doc
defining-models
:name]の章でこれらのメソッドすべてが使われている例を学びます。
モデルを生成する
Doctrineは使い始めを楽にするためにこれらのクラスを生成する方法を提供します。
NOTE 既存のデータベースの生成は始めるための利便性のみしか意味しません。データベースを生成した後で必要に応じて調整と整頓をしなければなりません。
既存のデータベース
よくある事例はORMにアクセスするデータベースとコードはより巨大/複雑になることです。SQLを手で書くよりも信頼のおけるツールが必要です。
Doctrineは既存のデータベースからDoctrine\_Record
クラスを生成する機能をサポートします。ドメインモデル用にすべてのDoctrine_Record
クラスを手動で書く必要はありません。
最初のインポートを行う
doctrine_test
という名前のデータベースとuser
という名前の単独のテーブルがある場合を考えてみましょう。user
テーブルは次のSQL文で作成されます:
CREATE TABLE user ( id bigint(20) NOT NULL auto_increment, first_name
varchar(255) default NULL, last_name varchar(255) default NULL, username varchar(255) default NULL, password varchar(255) default NULL, type varchar(255) default NULL, is_active tinyint(1) default '1', is_super_admin tinyint(1) default '0', created_at TIMESTAMP, updated_at TIMESTAMP, PRIMARY KEY (id) ) ENGINE=InnoDB
これをDoctrine_Record
クラスに変換することを考えます。Doctrineによってこれは簡単です![doc
getting-started
:name]章で作成したテストスクリプトを覚えていますか?これを利用してモデルを生成します。
最初にSQLiteのメモリの代わりにMySQLデータベースを使うためにbootstrap.php
を修正する必要があります:
// bootstrap.php
// ... $conn = Doctrine_Manager::connection('mysql://root:mys3cr3et@localhost/doctrine_test', 'doctrine'); // ...
NOTE データベースがまだ存在せず接続ユーザーがデータベースを作成するパーミッションを持つ場合データベースを作成するために
$conn->createDatabase()
メソッドを使うことができます。テーブルを作成するために`CREATE TABLE
`文を使用します。
生成クラスを置く場所が必要なのでdoctrine_test
ディレクトリの中でmodels
という名前のディレクトリを作りましょう:
$ mkdir doctrine_test/models
モデルクラスを生成するにはtest.php
スクリプトにコードを追加することだけが必要です:
// test.php
// ... Doctrine_Core::generateModelsFromDb('models', array('doctrine'), array('generateTableClasses' => true));
NOTE
generateModelsFromDb
メソッドは1つのパラメータのみを必要としこのパラメータはディレクトリです(生成レコードが書き込まれるディレクトリ)。2番目の引数はモデルを生成するためのデータベースの接続名の配列で、3番目はモデルのビルド用のオプションの配列です。
これだけです!doctrine_test/models/generated
ディレクトリでBaseUser.php
という名前のファイルがあります。ファイルは次のようになります:
// models/generated/BaseUser.php
/** * This class has been auto-generated by the Doctrine ORM Framework */ abstract class BaseUser extends Doctrine_Record { public function setTableDefinition() { $this->setTableName('user'); $this->hasColumn('id', 'integer', 8, array('type' => 'integer', 'length' => 8, 'primary' => true, 'autoincrement' => true)); $this->hasColumn('first_name', 'string', 255, array('type' => 'string', 'length' => 255)); $this->hasColumn('last_name', 'string', 255, array('type' => 'string', 'length' => 255)); $this->hasColumn('username', 'string', 255, array('type' => 'string', 'length' => 255)); $this->hasColumn('password', 'string', 255, array('type' => 'string', 'length' => 255)); $this->hasColumn('type', 'string', 255, array('type' => 'string', 'length' => 255)); $this->hasColumn('is_active', 'integer', 1, array('type' => 'integer', 'length' => 1, 'default' => '1')); $this->hasColumn('is_super_admin', 'integer', 1, array('type' => 'integer', 'length' => 1, 'default' => '0')); $this->hasColumn('created_at', 'timestamp', null, array('type' => 'timestamp', 'notnull' => true)); $this->hasColumn('updated_at', 'timestamp', null, array('type' => 'timestamp', 'notnull' => true)); } }
doctrine_test/models
ディレクトリでUser.php
という名前のファイルもあります。ファイルは次のようになります:
// models/User.php
/** * This class has been auto-generated by the Doctrine ORM Framework */ class User extends BaseUser {
}
Doctrineはdoctrine\_test/models/UserTable.php
でDoctrine_Table
スケルトンクラスを自動生成します。true
の値を持つgenerateTableClasses
オプションを渡したからです。ファイルは次のようになります:
// models/UserTable.php
/** * This class has been auto-generated by the Doctrine ORM Framework */ class UserTable extends Doctrine_Table {
}
モデルの機能をカスタマイズするためにUser
とUserTable
クラスの中でカスタムメソッドを設置できます。下記のコードは例です:
// models/User.php
// ... class User extends BaseUser { public function
setPassword($password) { return this->_set('password', md5(
password));} }
適切に動作させるためにpassword
アクセサをオーバーライドするにはbootstrap.php
ファイルでauto\_accessor_override
属性を有効にしなければなりません。
// bootstrap.php
// ... $manager->setAttribute(Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
ユーザーパスワードを設定しようとするとmd5に暗号化されます。最初にmodels
ディレクトリからモデルをオートロードするために次のようにbootstrap.php
ファイルを修正する必要があります:
// bootstrap.php
// ... Doctrine_Core::loadModels('models');
NOTE モデルのロードはこの章の[doc introduction-to-models:autoloading-models :name]セクションで説明されます。
User
モデルに行った変更をテストするコードを含めるためにtest.php
を修正します:
// test.php
// ...
$user = new User(); $user->username = 'jwage'; $user->password = 'changeme';
echo $user->password; // changemeではなくmd5ハッシュを出力する
ターミナルからtest.php
を実行するとき次の内容が表示されます:
$ php test.php 4cb9c8a8048fd02294477fcb1a41191a
UserTable
クラスに追加するカスタムメソッドの例は次の通りです:
// models/UserTable.php
// ... class UserTable extends Doctrine_Table { public function getCreatedToday() { $today = date('Y-m-d h:i:s', strtotime(date('Y-m-d'))); return $this->createQuery('u') ->where('u.created_at > ?', $today) ->execute(); } }
カスタムのDoctrine\_Table
クラスをロードするにはbootstrap.php
ファイルでautoload\_table_classes
属性を有効にしなければなりません。
// boostrap.php
// ... $manager->setAttribute(Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES, true);
UserTable
インスタンスを扱っているときにこのメソッドにアクセスできます:
// test.php
// ... $usersCreatedToday = Doctrine_Core::getTable('User')->getCreatedToday();
スキーマファイル
代わりにYAMLスキーマファイルでモデルを管理してそれらのファイルからPHPクラスを生成できます。最初に作業をやりやすくするために手元にある既存のモデルからYAMLスキーマファイルを生成しましょう。次のコードを内部に取り込むためにtest.php
を変更します:
// test.php
// ...
Doctrine_Core::generateYamlFromModels('schema.yml', 'models');
test.php
スクリプトを実行します:
$ php test.php
doctrine_test
ディレクトリのrootに作成されたschema.yml
という名前のファイルを見ます。内容は次の通りです:
User: tableName: user columns: id: type: integer(8) primary: true
autoincrement: true is_active: type: integer(1) default: '1' is_super_admin: type: integer(1) default: '0' created_at: type: timestamp(25) notnull: true updated_at: type: timestamp(25) notnull: true first_name: string(255) last_name: string(255) username: string(255) password: string(255) type: string(255)
有効なYAMLスキーマファイルが手元にあるので、ここからスキーマを維持管理してPHPクラスを生成できます。generate.php
という名前の新しいPHPスクリプトを作りましょう。このスクリプトはすべてを再生成しスクリプトが呼び出されるたびにデータベースを再インスタンス化します:
// generate.php
require_once('bootstrap.php');
Doctrine_Core::dropDatabases(); Doctrine_Core::createDatabases(); Doctrine_Core::generateModelsFromYaml('schema.yml', 'models'); Doctrine_Core::createTablesFromModels('models');
schema.yml
を変更してターミナルから次のコマンドを実行してモデルを再生成できます:
$ php generate.php
YAMLスキーマファイルをセットアップしてスキーマファイルを再生成したのでファイルの内容を少し整頓してDoctrineの力を利用しましょう:
User: actAs: [Timestampable] columns: is_active: type: integer(1)
default: '1' is_super_admin: type: integer(1) default: '0' first_name: string(255) last_name: string(255) username: string(255) password: string(255) type: string(255)
NOTE 変更の注意点:
1.) デフォルトなので明示的な
tableName
の定義を削除した。 2.)Timestampable
ビヘイビアを添付した。 3.) 主キーが定義されていない場合自動的に追加されるのでid
カラムを削除した。 4.)Timestampable
ビヘイビアで自動的に処理できるのでupdated\_at
とcreated_at
カラムを削除した。デフォルトを利用することでYAMLはきれいになりコアのビヘイビアを活用するほど自分自身で行わなければならない作業は少なくなります。
YAMLスキーマファイルからモデルを再生成します:
$ php generate.php
[doc yaml-schema-files 専用の章]でYAMLスキーマファイルに関する詳しい内容を学びます。
モデルを書く
オプションとしてすべてのコンビニエンスメソッドをスキップして独自のPHPコードだけでモデルを書くことができます。[doc defining-models :name]の章でモデルの構文のすべてを学びます。
モデルをオートロードする
Doctrineはモデルをロードするための方法を2つ:コンサーバティブ(遅延)ロード、アグレッシブロードを提供します。コンサーバティブロードは初期にはPHPファイルを必要としません。代わりにクラスの名前へのパスをキャッシュしこのパスはspl_autoload_register()で初期に登録したDoctrine_Core::autoload()
で使われます。両方のモデルのロード方法を利用した例は次の通りです。
コンサーバティブ
コンサーバティブ(conservative - 慎重な・控えめな)なモデルロードは本番環境では理想的なモデルのロードメソッドになりつつあります。このメソッドはモデルのロードが実行されるときすべてのモデルをロードする代わりに遅延ロードします。
コンサーバティブなモデルロードはそれぞれが1つのクラスを持ち、ファイルの名前はクラスから名付けなければなりません。例えば、User
というクラスがある場合、User.php
という名前のファイルに含まれなければなりません。
コンサーバティブなモデルロードを使うにはモデルロードの属性をコンサーバティブにする必要があります:
$manager->setAttribute(Doctrine_Core::ATTR_MODEL_LOADING,
Doctrine_Core::MODEL_LOADING_CONSERVATIVE);
NOTE 以前のステップで
bootstrap.php
ファイルでこの変更をすでに行っているので再度同じ変更する必要はありません。
Doctrine_Core::loadModels()
の機能を使うとき見つかるすべてのクラスは内部でキャッシュされるのでオートローダーは後でそれらを読み込むことができます。
Doctrine_Core::loadModels('models');
新しいクラス、例えばUser
クラスをインスタンス化するとき、オートローダーが起動しクラスが読み込まれます。
// Doctrine_Core::autoload()の呼び出しが行われクラスが読み込まれる
$user = new User();
上記でクラスをインスタンス化することでDoctrine\_Core::autoload()
の呼び出しが行われDoctrine_Core::loadModels()
のコールで見つかったクラスが読み込まれ利用可能になります。
NOTE 必要がないときにモデルをクラスをすべて読み込むと不要なオーバーヘッドが生じるので、必要なときだけ読み込みたい場合、とりわけ本番環境でコンサーバティブなモデルロードは推奨されます。
アグレッシブ
アグレッシブ(aggressive -
積極的な)なモデルロードはデフォルトのモデルロードメソッドでとても便利です。.php
拡張子を持つファイルをすべて探し読み込みます。Doctrineは継承を満たすことができないで、モデルが別のクラスを継承する場合、正しい順序でそれらのクラスを読み込むことはできません。なのですべての依存関係がそれぞれのクラスで満たされるようにするのはあなたの仕事です。
アグレッシブなモデルロードではファイルごとに複数のクラスを用意しファイルの名前はファイル内部のクラスの名前と関連する必要はありません。
アグレッシブなモデルロードの欠点はすべてのPHPファイルがすべてのリクエストに含まれるので、たくさんのモデルがある場合コンサーバティブなモデルロードを使うことをお勧めします。
アグレッシブなモデルロードを使うにはモデルロード属性をアグレッシブに設定する必要があります:
$manager->setAttribute(Doctrine_Core::ATTR_MODEL_LOADING,
Doctrine_Core::MODEL_LOADING_AGGRESSIVE);
TIP アグレッシブなモデルロードはデフォルトのロード属性なので使う場合は明示的に設定する必要はありません。
Doctrine_Core::loadModels()
の機能を使うとき見つかるすべてのクラスは直ちに読み込まれます:
Doctrine_Core::loadModels('/path/to/models');
まとめ
この章はこれまでで最もハードだと思いますが良い内容です。モデルの使い方、既存のデータベースからモデルを生成する方法、独自のモデルを書く方法、とモデルをYAMLスキーマファイルとして管理する方法を少し学びました。モデルディレクトリからモデルをロードする機能を実装するためにDoctrineのテスト環境も修正しました。
Doctrineのモデルのトピックは非常に大きいので開発者がすべての情報を吸収しやすいように章を3つのピースに分割します。[doc defining-models 次の章]においてモデルを定義するために使うAPIに入ります。