Practical Symfony #7: Symfonyで複雑なバリデーションを行う方法
Symfonyにおいてフォームのバリデーションは、通常、フォームにエンティティクラスを関連付け、このエンティティクラスに対するバリデーションとして設定します。バリデーションの設定は、エンティティクラスのフィールド単位で行います。エンティティクラスの各フィールドの定義行のアノテーションとしてバリデーションの設定を記述できます。 ただし実際のプロジェクトでは単一のフィールドのバリデーションだけではなく、複数のフィールドに横断するようなバリデーションルールも必要になるでしょう。こういったバリデーションが必要な場合の方法を紹介します。
- True制約を使う
- Callback制約を使う
True制約を使う
1つ目は、Symfony組み込みのTrue制約を使う方法です。Symfonyの制約(バリデータ)は、エンティティクラスのフィールドだけでなく、名前がgetやisで始まるメソッドに対しても設定できます。これとTrue制約を組み合わせることで、追加のバリデーションを手軽に作成できます。次のコードは、エンティティクラス内のtelフィールドとtelConfirmフィールドの値が一致するかどうかを検証しています。
use Symfony\Component\Validator\Constraints as Assert;
/* snip */
/**
* @Assert\True(message="電話番号の入力に誤りがあります。")
*/
public function isValidTel()
{
if ($this->getTel() !== $this->getTelConfirm()) {
return false;
}
return true;
}
このように、エンティティクラスにisから始まる名前のメソッド(上のコードではisValidTel)を定義し、メソッドのDocBlockに制約として利用するための設定を記述します。True制約の場合は、指定したメソッドの戻り値がtrueの場合は成功、falseの場合はエラーとなります。エラーとなった場合、エラーメッセージとしてTrue制約のmessageオプションに指定した文字列が表示されます。このメソッドはエンティティクラスの通常のメソッドと何も変わりませんので、エンティティクラスのプロパティなどに自由にアクセスできます。
単に「同じ値を2度入力し、同じであることを確認する」という目的のフィールドをフォームで使いたい場合は、repeatedを使うと簡単です。
Callback制約を使う
True制約を使うことで、任意のルールをバリデーションとして使えるようになりましたが、エラーメッセージがフォームの先頭部分に表示されてしまいます。エラーメッセージを特定のフィールドにきちんと表示したい場合は、Callback制約を使います。
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\ExecutionContext;
/**
* @Assert\Callback(methods={"shouldValidTel"})
*/
class Registration
{
/* snip */
/**
* @param ExcecutionContext $context
*/
public function shouldValidTel(ExecutionContext $context)
{
if ($this->getTel() !== $this->getTelConfirm()) {
$propertyPath = $context->getPropertyPath() . '.telConfirm';
$context->setPropertyPath($propertyPath);
$context->addViolation('電話番号の入力に誤りがあります。', array(), null);
return;
}
}
}
Callback制約は、エンティティクラスのフィールドやメソッドではなく、クラスそのものに対して設定します。Callback制約のmethodsオプションに、バリデーション実行時に呼び出したいメソッドの名前を指定します。ここで呼び出すメソッドの名前には特に制限はありません。
呼び出されるメソッド側では、ExecutionContextオブジェクトを引数で受け取ります。バリデーションエラーの場合、メソッドの戻り値で何か値を返すのではなく、ExecutionContextオブジェクトへ、対象となるフィールドのパスとエラーメッセージを設定します。
Callback制約で呼び出されるメソッドでは、何もreturnしない点に注意してください。
まとめ
Symfonyのフォームで複数のフィールドに対するバリデーションを記述する方法について、True制約を使う方法とCallback制約を使う方法を紹介しました。
Symfonyのフォームでは、データの保持をエンティティクラスに分離できることが大きな特徴です。これにより、コントローラ等からの疎結合を維持したまま、データのバリデーションなどにドメインロジックを直接的に利用できます。