Symfonyで複数フィールドを同時にバリデーションする方法を徹底解説!初心者にもやさしく解説
生徒
「Symfonyでフォームを作ったときに、複数の入力項目の関係をチェックすることってできますか?」
先生
「はい、Symfonyのバリデーションには、複数フィールドを同時にチェックする方法も用意されています。」
生徒
「例えば、パスワードと確認用パスワードが一致しているか確認したいんです!」
先生
「その場合は、クラスレベルのバリデーションという方法を使います。仕組みや書き方を一緒に学びましょう!」
1. Symfonyで複数フィールドをチェックしたいとき
通常のSymfonyのバリデーションは、1つのフィールド(項目)に対してルールを設定します。たとえば「名前は必須」や「メールアドレスの形式が正しいか」などです。
しかし、時には複数の項目の関係をチェックしたいことがあります。よくある例は以下のようなケースです。
- パスワードと確認用パスワードが一致しているか
- 開始日と終了日の順番が正しいか
- 「はい」と答えたら、別の項目も必須にする
このようなチェックをしたいときは、「クラス全体に対してバリデーションをかける」というアプローチを使います。
2. クラスレベルバリデーションとは?
クラスレベルバリデーションとは、1つのフィールドだけでなく、エンティティ全体を見てルールを定義する方法です。
Symfonyでは、@Assert\Callbackという特殊なアノテーションを使うことで、独自のバリデーションメソッドを定義することができます。
そのメソッド内で、必要な複数のフィールドの値を比較し、ルールに違反しているかどうかを判断します。
3. 実際に書いてみよう!パスワード一致チェック
ここでは「パスワード」と「パスワード確認」の2つのフィールドが一致しているかをチェックする実例を紹介します。
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
use Symfony\Component\Validator\Constraints\Callback;
class UserRegistration
{
/**
* @Assert\NotBlank()
*/
private $password;
/**
* @Assert\NotBlank()
*/
private $confirmPassword;
/**
* @Assert\Callback
*/
public function validatePasswords(ExecutionContextInterface $context): void
{
if ($this->password !== $this->confirmPassword) {
$context->buildViolation('パスワードが一致しません。')
->atPath('confirmPassword')
->addViolation();
}
}
}
このコードでは、validatePasswords()というメソッドを作成し、@Assert\Callbackで呼び出しています。
2つのパスワードが一致しない場合にだけ、Symfonyが自動的にエラーを表示してくれます。
4. atPath()とは?エラーの場所を指定する
atPath()は、「どのフィールドにエラーメッセージを表示するか」を指定するメソッドです。
先ほどの例では、確認用パスワード(confirmPassword)にだけエラーメッセージを表示させたいので、->atPath('confirmPassword')と指定しています。
この指定をしないと、エラーがクラス全体に紐づいてしまい、ユーザーが混乱してしまう可能性があります。
5. Twigテンプレートでエラーを表示する
Symfonyでは、フォームのテンプレートで{{ form_row() }}を使えば、自動でエラーメッセージを表示してくれます。
以下のように書くことで、確認用パスワードにエラーがあればその場に表示されます。
{{ form_start(form) }}
{{ form_row(form.password) }}
{{ form_row(form.confirmPassword) }}
<button type="submit">登録</button>
{{ form_end(form) }}
6. 開始日と終了日の順番チェックも可能
他のよくあるパターンとして、「開始日が終了日より後だったらエラー」というチェックもできます。
以下はその一例です。
/**
* @Assert\Callback
*/
public function validateDates(ExecutionContextInterface $context): void
{
if ($this->startDate > $this->endDate) {
$context->buildViolation('開始日は終了日より前にしてください。')
->atPath('startDate')
->addViolation();
}
}
このように、クラスの中で複数のプロパティを比較して、自由にバリデーションを設計できるのがSymfonyの強みです。
7. クラスレベルのバリデーションで注意すべきこと
以下のポイントに気をつけると、スムーズに複数フィールドのバリデーションができます。
@Assert\Callbackのメソッド名は自由に決めてOK- バリデーションメソッドには必ず
ExecutionContextInterfaceを引数に入れる use文を忘れるとエラーになるので注意
また、バリデーションの対象クラスがコントローラで使われているか、フォームと連携しているかも確認しておきましょう。