我有一个表单“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';
}
}
当您尝试嵌入城市字段并且实体没有填充城市值时,方法$ form-> getData()返回null,因此在这种情况下city字段将不会呈现,因为“if”语句始终返回false(preSetData函数in订阅者类)首先我们要检查城市是否填满,如果是,请添加相应的表单字段,否则添加空白字段(或其他)。
以下是一些代码段:
public function preSetData(DataEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
if (!($data instanceof Address) || !$data->getCity()) {
return;
}
$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,
)));
}
public function postSetData(DataEvent $event)
{
$form = $event->getForm();
if (!$form->has('city_selector')) {
$form->add($this->factory->createNamed('city_selector', 'city', null, array(
'choices' => array('' => '-- Selecione um estado --'),
'required' => true,
'expanded' => false,
'multiple' => false,
)));
}
}
public static function getSubscribedEvents()
{
return array(
FormEvents::PRE_SET_DATA => 'preSetData',
FormEvents::POST_SET_DATA => 'postSetData'
);
}