LoginSignup
7
6

More than 5 years have passed since last update.

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

Posted at

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するところなどポイントは多々ありますが、テンプレを作って動くサンプルを作ることがポイントね。

7
6
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
6