Symfony2でFormTypeからContainerを参照する


[前回](http://qiita.com/adarah_g/items/d2a534f127fb641bb511 "Symfony2でEntityRepositoryからContainerを参照できるようにする")はEntityRepositoryからContainerを参照できるようにしました。
今回はFormTypeからContainerを参照できるようにします。

## 環境
Symfony 2.3.17

## 実装する
### Containerを参照可能なFormTypeクラスを作成

```php:AbstractContainerAwareType.php
<?php

namespace Some\MyBundle\Component\Form;

use Symfony\Component\DependencyInjection\ContainerAwareInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\AbstractType;

abstract class AbstractContainerAwareType extends AbstractType implements ContainerAwareInterface
{
    /**
     * Container
     * @var ContainerInterface
     */
    protected $container;

    /**
     * {@inheritdoc}
     */
    public function setContainer(ContainerInterface $container = null)
    {
        $this->container = $container;
    }

    /**
     * Get Container
     * @return ContainerInterface
     */
    public function getContainer()
    {
        return $this->container;
    }
}
```

### AbstractContainerAwareTypeにContainerを設定するFormFactoryクラスを作成

```php:MyFormFactory.php
<?php

namespace Some\MyBundle\Component\Form;

use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\Form\FormFactory;
use Symfony\Component\Form\FormRegistryInterface;
use Symfony\Component\Form\ResolvedFormTypeFactoryInterface;

class MyFormFactory extends FormFactory
{
    /**
     * Container
     * @var ContainerInterface
     */
    private $container;

    /**
     * @param FormRegistryInterface $registry
     * @param ResolvedFormTypeFactoryInterface $resolvedTypeFactory
     * @param ContainerInterface $container
     */
    public function __construct(FormRegistryInterface $registry, ResolvedFormTypeFactoryInterface $resolvedTypeFactory, ContainerInterface $container)
    {
        parent::__construct($registry, $resolvedTypeFactory);
        $this->container = $container;
    }

    /**
     * {@inheritdoc}
     */
    public function createNamedBuilder($name, $type = 'form', $data = null, array $options = array())
    {
        if ($type instanceof AbstractContainerAwareType) {
            $type->setContainer($this->container);
        }

        return parent::createNamedBuilder($name, $type, $data, $options);
    }
}
```

サービスに登録

```yaml:services.yml
services:
    form.factory: # FrameworkBundleで定義されているものを再定義
        class: Some\MyBundle\Component\Form\MyFormFactory
        arguments: [ @form.registry, @form.resolved_type_factory, @service_container ]
```

## 使用する
AbstractContainerAwareTypeを継承したクラス内で`$this->getContainer()`するとContainerを参照できます。

```php:SomeType.php
<?php

namespace Some\MyBundle\Form\Type;

use Some\MyBundle\Component\Form\AbstractContainerAwareType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class SomeType extends AbstractContainerAwareType
{
    /**
     * {@inheritdoc}
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $container = $this->getContainer();

        $builder
            ->add('someColumn', 'choice', [
                'choices' => $container->getParameter('some.parameter'),
            ])
        ;

        $logger = $container->get('logger');
        $logger->debug('FormTypeからログ出力!');
    }

    /**
     * {@inheritdoc}
     */
    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver
            ->setDefaults([
                'data_class' => 'Some\MyBundle\Entity\SomeTable',
            ])
        ;
    }

    /**
     * {@inheritdoc}
     */
    public function getName()
    {
        return 'some';
    }
}
```