Symfony2: FormTypeにサービスコンテナなどをインジェクションする方法


DoctrineなどのQueryBuilderで取得した項目をフォームで利用するなど当たり前の事なのですが、これがSymfony2ではインジェクションをしないことには実装できません。意外とややこしいのです。

例えば

class ExampleType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $em = $this->getDoctrine()->getManager();
        $entities = $em->getRepository('AcmeUserBundle:user')->findAll();
        
        $choices = array();
        foreach($entities as $entity)
        {
            $choices[$entity->getId()] = $entity->getName();
        }
        $builder
            ->add('column_name', 'choice', array( 'choices' => $choices )))
        ;
    }

なんて事ができればいいのですが、インジェクションしないとこれは動きません。で!どのようにするかというと、service.yml に インジェクションするコンストラクタを設定定義してます。この定義した内容を formTypeをnewする際にコントローラー側から getします。コード見ないときっとわかりにくいかと思います。

まずは service.yml ですが、各bundle内でも構いませんし、appの中でも構いません。bundleの中に記述したほうが分かりやすいと思ってます。

service.yml の内容

services:
    acme_user.user.form.type:
        class: Acme\UserBundle\Form\UserType
        arguments: ["@doctrine.orm.entity_manager", "@service_container"]
        tags:
            - { name: form.type, alias: app_user_user }

@doctrine.orm.entity_manager
@service_container

は php app/console container:debug で一覧表示できますが、細かい動作の内容についてはsensioのソースの中を調べることになります。自分で書いたservice.ymlも同じようにコンストラクタとしてインジェクション出来ます。便利ですね。

続いてformTypeクラスのコンストラクタを記述します。先の動作しないformTypeを書き換えます。__construct を実装している所がポイントです。

class ExampleType extends AbstractType
{
    private $doctrine, $service_container;
    // private $user;

    public function __construct($doctrine, $service_container)
    {
        $this->doctrine = $doctrine;
        $this->service_container = $service_container;
        /* 下記のように予め書いておくことでUserにもアクセスできます。 */
        // $this->user = $service_container->get('security.context')->getToken()->getUser();
    }
    
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $entities = $this->doctrine->getRepository('AcmeUserBundle:user')->findAll();
        
        $choices = array();
        foreach($entities as $entity)
        {
            $choices[$entity->getId()] = $entity->getName();
        }
        $builder
            ->add('column_name', 'choice', array( 'choices' => $choices )))
        ;
    }
    public function getName()
    {
        return 'app_user_user'; /* service.ymlのalias名を入力 */
    }

このformTypeをコントローラーからアクセスします。

$form = $this->createForm($this->get('acme_user.user.form.type'), $entity, array(
    'action' => $this->generateUrl('user_create'), // you should fix
    'method' => 'POST',
));

という感じで $this->get(‘service.ymlで定義した名前’); という風に クラスを new します。すると service.yml 経由で目的のクラスをインジェクションすることが出来ます。

多用するの結構面倒なんですよね。service.ymlの記述とコンストラクタをつくるところや、formtypeをnewするところなどポイントは多々ありますが、テンプレを作って動くサンプルを作ることがポイントね。

  • このエントリーをはてなブックマークに追加

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です