せっかくEntityにNotBlankアノテーションなりMaxLengthアノテーションなりを付与しているにもかかわらず、Symfony2はそれをバリデーションに使用するばかりでFormBuilderでは参照してくれない。そのため、たとえばinput要素にmax_length属性を付与する場合はFormBuilder.addメソッド呼び出し時に引数optionにmax_lengthやrequiredなどを改めて指定しなければならない。これではEntityのアノテーションを修正した際に、FormBuilderを使用している箇所も併せて修正しなければならなくなってしまい、保守性が悪い。
そこで、アプリケーションで使用する基底Controllerに下記のようなメソッドを追加し、Entityに付与されているアノテーションからFormBuilderに渡す引数optionを生成できるようにしてみた。
BaseController.php:
<?php namespace Jibunstyle\ZenbunBundle\Controller; use Symfony\Bundle\FrameworkBundle\Controller\Controller; use \Symfony\Component\Form\FormBuilder; use Doctrine\Common\Annotations\AnnotationReader; abstract class BaseController extends Controller { protected function getFormOptionFromEntity($entityObject, $propertyName) { $refClass = new \ReflectionClass($entityObject); $reader = new AnnotationReader(); $refProperty = $refClass->getProperty($propertyName); $propertyAnnotations = $reader->getPropertyAnnotations($refProperty); $options = array(); foreach ($propertyAnnotations as $annotation) { if ($annotation instanceof \Symfony\Component\Validator\Constraints\NotBlank) { $options['required'] = TRUE; } else if ($annotation instanceof \Symfony\Component\Validator\Constraints\MaxLength) { $options['max_length'] = $annotation->limit; } } return $options; } }
UserController.php:
<?php (略) private function createRegisterForm($entityObject, $propertyList) { $user = new User(); return $this->createFormBuilder($user) ->add('id', 'email', $this->getFormOptionFromEntity($user, 'id')) ->add('name', 'text', $this->getFormOptionFromEntity($user, 'name')) ->add('password', 'password', $this->getFormOptionFromEntity($user, 'password')) ->getForm(); } (略)
これでEntityのアノテーションからOptionを作るという当初の目的は達成できるが、getFormOptionFromEntityメソッドに改めてEntityとプロパティ名を記述しなければならないのがダサい。FormBuilderのaddメソッドはEntityもプロパティ名もすでに知っているため、カスタムFormBuilderを実装し、それのaddメソッドをオーバーライドして、optionが明示的に与えられなかった場合はgetFormOptionFromEntityを呼ぶような動作にすれば凄くきれいだと思う。が、カスタムFormBuilderをSymfonyのお作法に則って作成する方法をまだ調べられていないのでこの辺で。