【问题标题】:Symfony2 Form - Embed forms and event listenersSymfony2 Form - 嵌入表单和事件监听器
【发布时间】:2012-09-06 21:06:27
【问题描述】:

我有一个带有事件侦听器的表单“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';
    }
}

【问题讨论】:

    标签: php symfony symfony-forms


    【解决方案1】:

    当您尝试嵌入城市字段并且实体没有填充城市值时,方法$form->getData()返回null,因此在这种情况下城市字段不会呈现,因为“if”语句总是返回false(订阅者类中的 preSetData 函数) 首先我们要检查是否填写了城市,如果是则添加相应的表单字段,否则添加空白字段(或其他)。

    这里有一些代码sn-ps:

    1. 如果填写了城市字段,则添加城市字段

      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,
          )));
      }
      
    2. 如果值未填写则添加空白城市字段(postSetData在设置数据后触发)

      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,
              )));
          }
      }
      
    3. 我们还要注册 postSetData 事件

      public static function getSubscribedEvents()
      {
          return array(
              FormEvents::PRE_SET_DATA   => 'preSetData',
              FormEvents::POST_SET_DATA  => 'postSetData'
          );
      }
      

    【讨论】:

    • 欢迎来到 Stack Overflow!不要只发布一段代码,请解释为什么这段代码可以解决所提出的问题。没有解释,这不是答案。
    • 谢谢!我会尽量听从你的建议,希望我的英语没那么差。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-09-30
    • 1970-01-01
    • 2016-11-30
    • 2014-02-21
    • 2014-12-03
    • 2015-03-10
    相关资源
    最近更新 更多