スキーマファイルの目的はPHPコードの編集よりもYAMLファイルでモデルの定義を直接管理できるようにすることです。すべてのモデルの定義/クラスを生成するためにYAMLスキーマファイルは解析され使われます。これによってDoctrineモデルの定義がはるかにポータブルなものになります。
スキーマファイルはPHPコードで書く通常のすべての内容をサポートします。接続バインディング用のコンポーネント、リレーション、属性、テンプレート/ビヘイビア、インデックスなどがあります。
Doctrineは省略記法でスキーマを指定する機能を提供します。多くのスキーマパラメータはデフォルトの値を持ち、これによって構文をできるのでDoctrineはデフォルトを利用できます。すべての省略記法を利用したスキーマの例は下記の通りです。
detect_relationsオプションによってカラムの名前からリレーションの推測が行われます。下記の例ではDoctrine はUserが1つのContactを持つことを知っておりモデルの間のリレーションを自動的に定義します。
---
detect_relations: true
User:
columns:
username: string
password: string
contact_id: integer
Contact:
columns:
first_name: string
last_name: string
phone: string
email: string
address: string
上記のスキーマを100%冗長にしたものは次の通りです:
---
User:
columns:
username:
type: string(255)
password:
type: string(255)
contact_id:
type: integer
relations:
Contact:
class: Contact
local: contact_id
foreign: id
foreignAlias: User
foreignType: one
type: one
Contact:
columns:
first_name:
type: string(255)
last_name:
type: string(255)
phone:
type: string(255)
email:
type: string(255)
address:
type: string(255)
relations:
User:
class: User
local: id
foreign: contact_id
foreignAlias: Contact
foreignType: one
type: one
上記の例ではdetect_relationsオプションを定義せず、代わりにローカル/外部キー、型、とそれぞれの側のリレーションのエイリアスの設定を通して完全にコントロールできるように手動でリレーションを定義します。
リレーションを指定するとき外部キーが存在している方のリレーションを指定することだけが必要です。スキーマファイルが解析されるとき、Doctrineはリレーションを反映し反対側を自動的にビルドします。もう一方のリレーションを指定する場合、自動生成は行われません。
前に見たようにDoctrineはdetect_relationsオプションを指定する機能を提供します。この機能はカラムの名前に基づいてリレーションを自動的に構築します。contact_idを持つUserモデルとContactという名前を持つクラスが存在する場合、2つの間のリレーションが自動的に作成されます。
Doctrineは外部キーが存在している側のリレーションを指定することのみを要求します。リレーションの反対側は反映されもう一方側に基づいてビルドされます。スキーマ構文はリレーションのエイリアスと反対側の型をカスタマイズする機能を提供します。すべての関連するリレーションを一箇所で維持できるのでこれはよいニュースです。下記の内容はエイリアスと反対側のリレーションの型をカスタマイズする方法です。これはUserが1つのContactを持ちContactは1つのUserをUserModelとして持つというリレーションを示しています。通常は自動生成されたUserは1つのContactを持ちContactは複数のUserを持ちます。foreignTypeとforeignAliasオプションによって反対側のリレーションをカスタマイズできます。
---
User:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
relations:
Contact:
foreignType: one
foreignAlias: UserModel
Contact:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
name:
type: string(255)
次のようにthe detect_relationsオプションを持つ2つのモデルの間のリレーションを見つけて作成できます。
---
detect_relations: true
User:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
avatar_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
Avatar:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
name:
type: string(255)
image_file:
type: string(255)
結果のリレーションはUserは1つのAvatarを持ちAvatarは複数のUserを持ちます。
---
User:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
relations:
Contact:
foreignType: one
Contact:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
name:
type: string(255)
---
User:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
Phonenumber:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
name:
type: string(255)
user_id:
type: integer(4)
relations:
User:
foreignAlias: Phonenumbers
---
User:
columns:
id:
type: integer(4)
autoincrement: true
primary: true
username:
type: string(255)
password:
type: string(255)
attributes:
export: all
validate: true
Group:
tableName: group_table
columns:
id:
type: integer(4)
autoincrement: true
primary: true
name:
type: string(255)
relations:
Users:
foreignAlias: Groups
class: User
refClass: GroupUser
GroupUser:
columns:
group_id:
type: integer(4)
primary: true
user_id:
type: integer(4)
primary: true
relations:
Group:
foreignAlias: GroupUsers
User:
foreignAlias: GroupUsers
この場合Userは複数のGroupsを持ち、Groupは複数のUsersを持ち、GroupUserは1つのUserを持ちGroupUserは1つのGroupを持つモデルのセットが作られます。
モデルを管理するためにスキーマファイルを使わないのであれば、通常は次のコードのようにコンポーネントを接続名にバインドするために使います:
下記のように接続を作成します:
Doctrine_Manager::connection('mysql://jwage:pass@localhost/connection1', 'connection1');
Doctrineのブートストラップスクリプトでモデルをその接続にバインドします:
Doctrine_Manager::connection()->bindComponent('User', 'conn1');
スキーマファイルは接続パラメータを指定することでこれを特定の接続にバインドする機能を提供します。接続を指定しなければモデルはDoctrine_Managerインスタンスにセットあれた現在の接続を使います。
---
User:
connection: connection1
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
Doctrine_Record子クラスを手作業で書いたのと同じようにDoctrineはスキーマファイルで生成モデル用の属性を直接設定する手段を提供します。
---
User:
connection: connection1
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
attributes:
export: none
validate: false
スキーマファイルでenumカラムを使うために型をenumとして指定し可能なenumの値として値の配列を指定しなければなりません。
---
TvListing:
tableName: tv_listing
actAs: [Timestampable]
columns:
notes:
type: string
taping:
type: enum
length: 4
values: ['live', 'tape']
region:
type: enum
length: 4
values: ['US', 'CA']
actAsオプションでモデルにビヘイビアを取り付けることができます:
---
User:
connection: connection1
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
actAs:
Timestampable:
Sluggable:
fields: [username]
name: slug # defaults to 'slug'
type: string # defaults to 'clob'
length: 255 # defaults to null. clob doesn't require a length
何も指定しない場合デフォルトの値が使われるのでSluggableビヘイビアで指定されたオプションはオプションです。これらはデフォルトなので毎回入力する必要はありません。
---
User:
connection: connection1
columns:
# ...
actAs: [Timestampable, Sluggable]
モデルに取り付けたいリスナーがある場合、同じようにYAMLファイルで直接これらを指定できます。
---
User:
listeners: [ MyCustomListener ]
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
上記の構文で次のような基底クラスが生成されます:
class BaseUser extends Doctrine_Record
{
// ...
public setUp()
{
// ...
$this->addListener(new MyCustomListener());
}
}
テーブル用のオプションを指定するとDoctrineがモデルからテーブルを作成するときにオプションはcreate tableステートメントに設定されます。
---
User:
connection: connection1
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
options:
type: INNODB
collate: utf8_unicode_ci
charset: utf8
インデックスとオプションの詳細情報はchapterの[doc defining-models:indexes :name]セクションを参照してくださるようお願いします。
---
UserProfile:
columns:
user_id:
type: integer
length: 4
primary: true
autoincrement: true
first_name:
type: string
length: 20
last_name:
type: string
length: 20
indexes:
name_index:
fields:
first_name:
sorting: ASC
length: 10
primary: true
last_name: []
type: unique
インデックスの定義用のモデルクラスのsetTableDefinition()で自動生成されたPHPコードは次の通りです:
$this->index('name_index', array(
'fields' => array(
'first_name' => array(
'sorting' => 'ASC',
'length' => '10',
'primary' => true
),
'last_name' => array()),
'type' => 'unique'
)
);
下記のコードはYAMLスキーマファイルを使用して異なるタイプの継承をセットアップする方法を示しています。
---
Entity:
columns:
name: string(255)
username: string(255)
password: string(255)
User:
inheritance:
extends: Entity
type: simple
Group:
inheritance:
extends: Entity
type: simple
単一継承するモデルで定義されたカラムもしくはリレーションはPHPクラスが生成されたときに親に移動します。
[doc inheritance:simple :fullname]の章でこのトピックの詳細を読むことができます。
---
TextItem:
columns:
topic: string(255)
Comment:
inheritance:
extends: TextItem
type: concrete
columns:
content: string(300)
[doc inheritance:concrete :fullname]の章でこのトピックの詳細を読むことができます。
単一継承のように、PHPクラスが生成されたとき子に追加されるカラムもしくはリレーションは自動的に削除され親に移動します。
他のモデルが継承するEntityという名前のモデルを定義しましょう:
---
Entity:
columns:
name: string(255)
type: string(255)
typeカラムはオプションです。このカラムは子クラスで指定された場合自動的に追加されます。
Entityモデルを継承するUserモデルを作りましょう:
---
User:
inheritance:
extends: Entity
type: column_aggregation
keyField: type
keyValue: User
columns:
username: string(255)
password: string(255)
inheritance定義の下のtypeオプションはkeyFieldもしくはkeyValueを指定する場合暗示されるのでオプションです。keyFieldが指定されない場合デフォルトではtypeという名前のカラムが追加されます。何も指定しない場合デフォルトでkeyValueがモデルの名前になります。
再度Entityを継承するGroupという名前の別のモデルを作りましょう:
---
Group:
inheritance:
extends: Entity
type: column_aggregation
keyField: type
keyValue: Group
columns:
description: string(255)
UserのusernameとpasswordとGroupのdescriptionカラムは自動的に親のEntityに移動します。
[doc inheritance:column-aggregation :fullname]で詳細トピックを読むことができます。
データベースのカラム名以外のカラム名のエイリアスを作成したい場合、Doctrineでこれを実現するのは簡単です。カラムの名前で"column_name as field_name"の構文を使います:
---
User:
columns:
login:
name: login as username
type: string(255)
password:
type: string(255)
上記の例ではusernameエイリアスからカラム名のloginにアクセスできます。
Doctrineはサブフォルダでモデルを生成する"package"パラメータを提供します。大きなスキーマファイルによってフォルダの内外でスキーマをよりよく編成できます。
---
User:
package: User
columns:
username: string(255)
このスキーマファイルからのモデルファイルはUserという名前のフォルダに設置されます。"package: User.Models"とさらにサブフォルダを指定すればモデルはUser/Modelsになります。
パッケージファイルを生成する完全なカスタムパスを指定することで適切なパスでパッケージを自動生成することもできます:
---
User:
package: User
package_custom_path: /path/to/generate/package
columns:
username: string(255)
Doctrineスキーマによってスキーマファイルで定義されたすべてのモデルに適用する特定のパラメータを指定できます。スキーマファイル用に設定できるグローバルパラメータの例が見つかります。
グローバルパラメータのリストは次の通りです:
| 名前 | 説明 |
|---|---|
| connection | モデルをバインドする接続名。 |
| attributes | モデル用の属性の配列 |
| actAs | モデル用のビヘイビアの配列 |
| options | モデル用のテーブルオプションの配列 |
| package | モデルを設置するパッケージ |
| inheritance | モデル用の継承情報の配列 |
| detect_relations | 外部キーのリレーションを検出するかどうか |
上記のグローバルパラメータをいつか使ったスキーマの例は次の通りです:
---
connection: conn_name1
actAs: [Timestampable]
options:
type: INNODB
package: User
detect_relations: true
User:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
contact_id:
type: integer(4)
username:
type: string(255)
password:
type: string(255)
Contact:
columns:
id:
type: integer(4)
primary: true
autoincrement: true
name:
type: string(255)
トップレベルのすべての設定はYAMLで定義されたすべてのモデルに適用されます。
一旦スキーマファイルを定義したらYAMLの定義からモデルをビルドするコードが必要です。
$options = array(
'packagesPrefix' => 'Plugin',
'baseClassName' => 'MyDoctrineRecord',
'suffix' => '.php'
);
Doctrine_Core::generateModelsFromYaml('/path/to/yaml', '/path/to/model', $options);
上記のコードは/path/to/generate/modelsのschema.yml用のモデルを生成します。
モデルのビルド方法をカスタマイズするために利用できる異なるオプションの表は次の通りです。packagesPrefix、baseClassNameとsuffixオプションを使用していることに注目してください。
| 名前 | デフォルト | 説明 |
|---|---|---|
| packagesPrefix | Package | ミドルパッケージモデルのプレフィックス |
| packagesPath | #models_path#/packages | パッケージファイルを書き込むパス |
| packagesFolderName | packages | パッケージパス内部で、パッケージを置くフォルダーの名前 |
| generateBaseClasses | true | 定義と空の基底モデルを継承するトップレベルのクラスを含めて抽象基底モデルを生成するかどうか |
| generateTableClasses | true | モデルごとにテーブルを生成するか |
| baseClassPrefix | Base | 生成既定モデルに使うプレフィックス |
| baseClassesDirectory | generated | 基底クラスの定義を生成するフォルダーの名前 |
| baseTableClassName | Doctrine_Table | ほかの生成テーブルクラス名が継承する基底テーブルクラス |
| baseClassName | Doctrine_Record | Doctrine_Record既定クラスの名前 |
| classPrefix | すべての生成クラスで使うプレフィックス | |
| classPrefixFiles | true | 生成ファイルの名前にもクラスのプレフィックスを使うかどうか |
| pearStyle | false | PEARスタイルのクラス名とファイルを生成するか。このオプションがtrueにセットされている場合。生成クラスファイルにおいてunderscores(_)はDIRECTORY_SEPARATORに置き換えられます。 |
| suffix | .php | 生成モデルに使う拡張子 |
| phpDocSubpackage | docブロックで生成するphpDocのサブパッケージ名 | |
| phpDocName | docブロックで生成するphpDocの著者名 | |
| phpDocEmail | docブロックで生成するphpDocのメール |
YAMLスキーマファイルのすべてを学んだのでData Validationに関する大きなトピックに移ります。これは重要なトピックです。ユーザーが入力したデータをあなた自身でバリデートしたくない場合データベースに永続的に保存する前にDoctrineにデータをバリデートさせます。