単独でインスタンス化したときに期待どおりに機能するイベントリスナーを備えたフォーム「AddressType」があります。これがコードです。
namespace Nc\ClientsBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Nc\ClientsBundle\Form\EventListener\AddCityFieldSubscriber;
class AddressType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder
->add('street')
->add('number', 'text')
->add('complement')
->add('district')
->add('state', 'entity', array(
'class' => 'ClientsBundle:State',
'property' => 'name',
));
$subscriber = new AddCityFieldSubscriber($builder->getFormFactory());
$builder->addEventSubscriber($subscriber);
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Nc\ClientsBundle\Entity\Address',
);
}
public function getName()
{
return 'addresstype';
}
}
<?php
namespace Nc\ClientsBundle\Form\EventListener;
use Symfony\Component\Form\Event\DataEvent;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;
use Doctrine\ORM\EntityRepository;
class AddCityFieldSubscriber implements EventSubscriberInterface
{
private $factory;
public function __construct(FormFactoryInterface $factory)
{
$this->factory = $factory;
}
public static function getSubscribedEvents()
{
// Tells the dispatcher that we want to listen on the form.pre_set_data
// event and that the preSetData method should be called.
return array(FormEvents::PRE_SET_DATA => 'preSetData');
}
public function preSetData(DataEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
// During form creation setData() is called with null as an argument
// by the FormBuilder constructor. We're only concerned with when
// setData is called with an actual Entity object in it (whether new,
// or fetched with Doctrine). This if statement let's us skip right
// over the null condition.
if (null === $data) {
return;
}
if (!$data->getCity()) {
$form->add($this->factory->createNamed('city_selector', 'city', null, array(
'choices' => array('' => '-- Selecione um estado --'),
'required' => true,
'expanded' => false,
'multiple' => false,
)));
} else {
$city = $data->getCity();
$form->add($this->factory->createNamed('city_selector', 'city', null, array(
'choices' => array($city->getId() => $city->getName()),
'required' => true,
'expanded' => false,
'multiple' => false,
)));
}
}
}
<?php
namespace Nc\ClientsBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Nc\ClientsBundle\Form\DataTransformer\CityToIdTransformer;
class CitySelectorType extends AbstractType
{
/**
* @var ObjectManager
*/
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
public function buildForm(FormBuilder $builder, array $options)
{
$transformer = new CityToIdTransformer($this->om);
$builder->prependNormTransformer($transformer);
}
public function getDefaultOptions(array $options)
{
return array(
'invalid_message' => 'Selecione um estado e uma cidade.',
);
}
public function getParent(array $options)
{
return 'choice';
}
public function getName()
{
return 'city_selector';
}
}
<?php
namespace Nc\ClientsBundle\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Doctrine\Common\Persistence\ObjectManager;
use Nc\ClientsBundle\Entity\City;
class CityToIdTransformer implements DataTransformerInterface
{
/**
* @var ObjectManager
*/
private $om;
/**
* @param ObjectManager $om
*/
public function __construct(ObjectManager $om)
{
$this->om = $om;
}
/**
* Transforms an object (city) to a integer (id).
*
* @param City|null $city
* @return string
*/
public function transform($city)
{
if ($city === null) {
return '';
}
return $city->getId();
}
/**
* Transforms a string (id) to an object (city).
*
* @param string $id
* @return City|null
* @throws TransformationFailedException if object (city) is not found.
*/
public function reverseTransform($id)
{
if (!$id) {
return null;
}
$city = $this->om
->getRepository('ClientsBundle:City')
->findOneBy(array('id' => $id))
;
if (null === $city) {
throw new TransformationFailedException(sprintf(
'An city with id "%s" does not exist!',
$id
));
}
return $city;
}
}
このフォームを「ClientType」フォームに埋め込もうとすると、「city」フィールドがレンダリングされません。
namespace Nc\ClientsBundle\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
class ClientType extends AbstractType
{
public function buildForm(FormBuilder $builder, array $options)
{
$builder->add('name');
$builder->add('type', 'choice', array(
'choices' => array('1' => 'Comum', '2' => 'Parceiro'),
'expanded' => true,
'multiple' => false,
'required' => true,
));
$builder->add('contactInfo', new ContactInfoType(), array('label' => ' '));
$builder->add('address', new AddressType(), array('label' => ' '));
}
public function getDefaultOptions(array $options)
{
return array(
'data_class' => 'Nc\ClientsBundle\Entity\Client',
);
}
public function getName()
{
return 'clienttype';
}
}