This project is no longer maintained and has been archived. |
はじめに
[http://www.postgresql.jp/document/pg820doc/html/ddl-constraints.html PostgreSQLのドキュメント]より引用: > データ型は、テーブルに格納するデータの種類を限定するための方法です。しかし、多くのアプリケーションでは、型が提供する制約では精密さに欠けます。例えば、製品の価格が入る列には、おそらく正数のみを受け入れるようにする必要があります。しかし、正数のみを受け入れるという標準のデータ型はありません。また、他の列や行に関連して列データを制約したい場合もあります。例えば、製品の情報が入っているテーブルでは、1つの製品番号についての行が2行以上あってはなりません。
Doctrineによってカラムとテーブルでポータブルな制約を定義できます。制約によってテーブルのデータを望むままにコントロールできます。ユーザーがカラムの制約に違反するデータを保存しようとすると、エラーが起動します。値がデフォルトの値の定義から来る場合でもこれが適用されます。
アプリケーションレベルのバリデータと同じようにDoctrineの制約はデータベースレベルの制約として振る舞います。このことは二重のセキュリティを意味します: 間違った種類の値とアプリケーションを許容しません。
Doctrineの範囲内で利用可能なバリデーションの全リストは次の通りです:
|| バリデータ(引数) || 制約 || 説明 || || notnull
||
NOT NULL
|| アプリケーションとデータベースの両方のレベルで'not
null'制約を確認する || || email
|| ||
値が有効なEメールであるかチェックする || || notblank
|| `NOT
NULL`` \|\| not blankであることをチェックする \|\| \|\| ``nospace
` ||
|| スペースがないことをチェックする || || past
||
CHECK
制約 || 値が過去の日付がチェックする || || future
|| || 値が未来の日付かチェックする || || minlength(length)
|| || 値が最小長を満たすかチェックする || || country
||
|| 値が有効な国コードであるかチェックする || || ip
|| ||
値が有効なIP(internet protocol)アドレスかチェックする || ||
htmlcolor
|| || 値が妥当なhtmlの色であるかチェックする || ||
range(min, max)
|| CHECK
制約 ||
値が引数で指定された範囲に収まるかチェックする || || unique
||
UNIQUE
制約 || 値がデータベーステーブルでユニークかチェックする
|| || regexp(expression)
|| ||
値が正規表現にマッチするかチェックする || || creditcard
||
|| 文字列が適正なクレジットカード番号であるかチェックする || ||
digits(int, frac)
|| 精度とスケール ||
値が整数の//int//の桁数を持ち分数の//frac//の桁数を持つかチェックする
|| || date
|| || 値が有効な日付であるかチェックする ||
readonly
|| ||
フィールドが修正され読み込み限定のフィールドに強制するようにfalseを返すかチェックする
|| || unsigned
|| || 整数値が符号無しであるかチェックする
|| || usstate
|| ||
値が有効なUSの州コードであるかチェックする ||
下記のコードはバリデータの使い方とカラムでバリデータ用の引数を指定する方法の例です。
minlength
バリデータを使う例です。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('username', 'string', 255, array(
'minlength' => 12
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: username: type: string(255) minlength: 12 # ...
= 例 =
Not Null
not-null
制約はカラムがnullの値を想定してはならないことを指定します。not-null
制約は常にカラムの制約として記述されます。
次の定義はカラム名用にnotnull
制約を使います。このことは指定されたカラムはnullの値を受け取らないことを意味します。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('username', 'string', 255, array(
'notnull' => true,
'primary' => true,
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます。:
# schema.yml
User: columns: username: type: string(255) notnull: true primary: true #
このクラスがデータベースにエクスポートされるとき次のSQL文が実行されます(MySQLにて):
CREATE TABLE user (username VARCHAR(255) NOT NULL, PRIMARY
KEY(username))
not-null制約はアプリケーションレベルのバリデータとして振る舞います。このことはDoctrineのバリデータが有効な場合、Doctrineは指定されたカラムを保存するときにnullの値が含まれないことを自動的にチェックします。
これらのカラムがnullの値を含む場合Doctrine\_Validator_Exception
が起動します。
Eメール
Eメールバリデータは入力された値が本当に有効なEメールアドレスでありアドレスドメイン用のMXレコードがEメールアドレスとして解決することをチェックします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('email', 'string', 255, array(
'email' => true
)
);
}
}
YAMLフォーマットでの同じサンプルは次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... email: type: string(255) email: true # ...
無効なEメールアドレスを持つユーザーを作成しようとするとバリデートは行われません:
// test.php
// ... $user = new User(); $user->username = 'jwage'; $user->email = 'jonwage';
if ( ! $user->isValid()) { echo 'User is invalid!'; }
jonwage
は有効なEメールアドレスではないので上記のコードは例外を投げます。これをさらに推し進めてEメールアドレスは有効であるがドメイン名が無効な例は次の通りです:
// test.php
// ... $user = new User(); $user->username = 'jwage'; $user->email = '[email protected]';
if ( ! $user->isValid()) { echo 'User is invalid!'; }
ドメインのsomefakedomainiknowdoesntexist.com
が存在せずPHPの`[http://www.php.net/checkdnsrr
checkdnsrr()]
`関数はfalseを返すので上記のコードはエラーになります。
Not Blank
not blankバリデータはnot nullバリデートと似ていますが空の文字列もしくは空白文字が含まれる場合はエラーになります。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('username', 'string', 255, array(
'notblank' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: username: type: string(255) notblank: true # ...
1つの空白スペースを含むusernameを持つUser
レコードを保存しようとすると、バリデーションはエラーになります:
// test.php
// ... $user = new User(); $user->username = ' ';
if ( ! $user->isValid()) { echo 'User is invalid!'; }
No Space
no spaceバリデータは単純です。値にスペースが含まれないことをチェックします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('username', 'string', 255, array(
'nospace' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: username: type: string(255) nospace: true # ...
スペースを含むusername
を持つUser
を保存しようとするとバリデーションが失敗します:
$user = new User(); $user->username = 'jon wage';
if ( ! $user->isValid()) { echo 'User is invalid!'; }
Past
pastバリデータは値が過去の有効な日付であるかをチェックします。この例ではbirthday
カラムを持つUser
モデルがあり日付が過去のものであることバリデートします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('birthday', 'timestamp', null, array(
'past' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... birthday: type: timestamp past: true # ...
過去にはない誕生日を設定しようとするとバリデーションエラーになります。
Future
futureバリデータはpastバリデータの反対でデータが未来の有効な日付であることをチェックします。この例ではnext\_appointment_date
カラムを持つUser
モデルがあり日付が未来のものであることをバリデートします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('next_appointment_date', 'timestamp', null, array(
'future' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... next_appointment_date: type: timestamp future: true # ...
予約日が未来のものでなければ、バリデーションエラーになります。
最小長
最小長は正確な表現ではありません。文字列の長さが最小の長さよりも大きいことをチェックします。この例ではpassword
カラムを持つUser
モデルがありpassword
の長さが少なくとも5文字であることを確認します。
// models/User.php
class User extends BaseUser { public function setTableDefinition() { parent::setTableDefinition();
// ...
$this->hasColumn('password', 'timestamp', null, array(
'minlength' => 5
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... password: type: timestamp minlength: 5 # ...
5文字より短いpassword
を持つUser
を保存しようとすると、バリデーションはエラーになります。
// test.php
// ... $user = new User(); $user->username = 'jwage'; $user->password = 'test';
if ( ! $user->isValid()) { echo 'User is invalid because "test" is only 4 characters long!'; }
Country
countryバリデータは値が有効なcountryコードであるかチェックします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('country', 'string', 2, array(
'country' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... country: type: string(2) country: true # ...
無効な国コードを持つUser
を保存しようとするとバリデーションがエラーになります。
// test.php
// ... $user = new User(); $user->username = 'jwage'; $user->country_code = 'zz';
if ( ! $user->isValid()) { echo 'User is invalid because "zz" is not a valid country code!'; }
IPアドレス
IPアドレスバリデータは値が有効なIPアドレスであることをチェックします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('ip_address', 'string', 15, array(
'ip' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... ip_address: type: string(15) ip: true # ...
無効なIPアドレスを持つUser
を保存しようとするとバリデーションはエラーになります。
$user = new User(); $user->username = 'jwage'; $user->ip_address =
'123.123';
if ( ! $user->isValid()) { echo 'User is invalid because "123.123" is not a valid ip address }
HTML Color
htmlcolorバリデータは値が有効な16進法のhtmlカラーであることをチェックします。
// models/User.php
class User extends BaseUser { public function setTableDefinition() { parent::setTableDefinition();
// ...
$this->hasColumn('favorite_color', 'string', 7, array(
'htmlcolor' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... favorite_color: type: string(7) htmlcolor: true #
favorite_color
カラム用の無効なhtmlカラーの値を持つUser
を保存しようとするとバリデーションはエラーになります。
// test.php
// ... $user = new User(); $user->username = 'jwage'; $user->favorite_color = 'red';
if ( ! $user->isValid()) { echo 'User is invalid because "red" is not a valid hex color'; }
Range
rangeバリデータは値が与えられた数の範囲にあることをチェックします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('age', 'integer', 3, array(
'range' => array(10, 100)
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... age: type: integer(3) range: [10, 100] # ...
10才未満もしくは100才を越えるUser
を保存しようとすると、バリデーションはエラーになります。
// test.php
// ... $user = new User(); $user->username = 'jwage'; $user->age = '3';
if ( ! $user->isValid()) { echo 'User is invalid because "3" is less than the minimum of "10"'; }
範囲配列の0
もしくは1
キーのどちらかを省略することで最大と最小の値をバリデートするためにrange
バリデータを使うことができます:
// models/User.php
class User extends BaseUser { public function setTableDefinition() { parent::setTableDefinition();
// ...
$this->hasColumn('age', 'integer', 3, array(
'range' => array(1 => 100)
)
);
}
}
上記の例では最大年齢は100才になります。最小値を指定するには、範囲配列で1
の代わりに0
を指定します。
YAML構文の例は次のようになります:
# schema.yml
User: columns: # ... age: type: integer(3) range: 1: 100 # ...
Unique
unique制約は1つのカラムもしくはカラムのグループに含まれるデータがテーブルのすべての列に関してユニークであること保証します。
一般的に、制約に含まれるカラムのすべての値が等しい複数の列が存在するときにunique制約は破られます。しかしながら、この比較では2つのnull値は等しいとはみなされません。このことはunique制約の下で制約の課された少なくとも1つのカラムでnull値を含む重複列を保存することが可能であることを意味します。このビヘイビアはSQL標準に準拠しますが、一部のデータベースはこのルールに従いません。ですのでポータルなアプリケーションを開発するときは注意してください。
次の定義はカラム名に対してunique
制約を使います。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('username', 'string', 255, array(
'unique' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: username: type: string(255) unique: true # ....
NOTE 主キーは既にuniqueなので主キー以外のカラムに対してのみunique制約を使うべきです。
正規表現
正規表現バリデータは独自の正規表現に対してカラムの値をバリデートするシンプルな方法です。この例ではユーザー名は有効な文字もしくは数字だけを含むことを確認します。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('username', 'string', 255, array(
'regexp' => '/[a-zA-Z0-9]/'
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: username: type: string(255) regexp: '/ [1]+$/' # ...
文字か数字以外の文字を含むusername
を持つUser
を保存しようとすると、バリデーションはエラーになります:
// test.php
// ... $user = new User(); $user->username = '[jwage';
if ( ! $user->isValid()) { echo 'User is invalid because the username contains a [ character'; }
クレジットカード
creditcardバリデータは値が本当に有効なクレジットカード番号であることをチェックします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('cc_number', 'integer', 16, array(
'creditcard' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
Read Only
readonly
バリデータが有効なカラムを修正しようとするとバリデーションに失敗します。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('readonly_value', 'string', 255, array(
'readonly' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... readonly_value: type: integer(16) readonly: true #
User
オブジェクトインスタンスからreadonly_value
という名前のカラムを修正しようとすると、バリデーションはエラーになります。
Unsigned
unsignedバリデータは整数が符号無しであることをチェックします。
// models/User.php
class User extends BaseUser { // ...
public function setTableDefinition()
{
parent::setTableDefinition();
// ...
$this->hasColumn('age', 'integer', 3, array(
'unsigned' => true
)
);
}
}
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
User: columns: # ... age: type: integer(3) unsigned: true # ...
マイナス年齢のUser
を保存しようとするとバリデーションはエラーになります:
// test.php
// ... $user = new User(); $user->username = 'jwage'; $user->age = '-100';
if ( ! $user->isValid()) { echo 'User is invalid because -100 is signed'; }
US State
usstateバリデータは文字列が有効なUSの州コードであることをチェックします。
// models/State.php
class State extends Doctrine_Record { public function setTableDefinition() { $this->hasColumn('name', 'string', 255); $this->hasColumn('code', 'string', 2, array( 'usstate' => true ) ); } }
YAMLフォーマットでの例は次の通りです。[doc yaml-schema-files :name]の章でYAMLの詳細を読むことができます:
# schema.yml
State: columns: name: string(255) code: type: string(2) usstate: true
無効な州コードでState
を保存しようとするとバリデーションがエラーになります。
$state = new State(); $state->name = 'Tennessee'; $state->code = 'ZZ';
if ( ! $state->isValid()) { echo 'State is invalid because "ZZ" is not a valid state code'; }
まとめ
データを永続的にデータベースに保存する前にDoctrineにデータのバリデーションを行わせる方法を理解しDoctrineコアが提供する共通のバリデータを使うことができます。
[doc inheritance 次の章]では[doc inheritance :name]を検討するので重要です!継承は最小のコードで複雑な機能を実現するための偉大な方法です。継承を検討した後で[doc behaviors :name]と呼ばれる継承よりも優れた機能を提供するカスタム戦略に移ります。