使用Symfony2.1表单时,我应该在什么时候将基于表单值的自定义数据(对象)绑定到表单的对象?

时间:2012-07-17 10:52:42

标签: symfony-forms symfony-2.1

鉴于以下情况,我应该在哪里放置逻辑以将Department绑定到Review:

实体:

  • 经销商(有很多部门)
  • 部门(有一种类型)
  • DepartmentType
  • 评论(有一个经销商和一个部门)

在我的ReviewForm上,我需要用户能够选择经销商和DepartmentType,然后在某种形式的回调或前/后绑定中,从他们那里找出要绑定到评论的部门。

我还需要在验证之前进行此操作,以便我可以验证该部门是经销商的孩子。

注意:审核与经销商和部门有关,当它 只能与部门联系以简化遍历和我正在进行的其他逻辑时。


到目前为止,我尝试过两种方法,但是达到了困难/混乱。

  • 表单上的DepartmentType上的DataTransformer,不确定我是否理解这一点,我的transform / reverseTransform方法是在Review对象中传递的,而不是字段对象。
  • PRE_BIND,在验证之前发生,但我只有原始数据可供使用,没有对象
  • POST_BIND,在验证后发生:(

对于验证关系的最后一步,我有一个相对简单的验证器应该可以完成这项工作,但我不确定在什么时候我打算将数据绑定到这样的对象。有什么指针吗?

2 个答案:

答案 0 :(得分:2)

由于验证也是在POST_BIND监听器中完成的,您可以简单地添加POST_BIND监听器,其优先级高于验证监听器(即任何> 0)。

如果你正在写一个听众:

$builder->addEventListener(FormEvents::POST_BIND, $myListener, 10);

如果你正在写订阅者:

public static function getSubscribedEvents()
{
    return array(
        FormEvents::POST_BIND => array('postBind', 10),
    );
}

public function postBind(FormEvent $event)
{
    ...
}

答案 1 :(得分:1)

我会使用标准(即:非Doctrine)选择类型,其中包含表示每个DepartmentType的选项。

然后使用DataTransformer将所选选项转换为相关类型,反之亦然。


您的自定义FormType应该看起来像这样:

class Department extends AbstractType
{
    private $em;

    public function __construct(EntityManager $em) {
        $this->em = $em;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $transformer = new DepartmentToTypeTransformer($this->em);
        $builder->addViewTransformer($transformer, true);

        $builder->getParent()->addEventListener(FormEvents::PRE_BIND, function($event) use ($transformer) {
            $data = (object) $event->getData();
            $transformer->setDealership($data->dealership);
        });
    }

    public function getParent()
    {
        return 'choice';
    }

    public function getName()
    {
        return 'department';
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $choices = array();

        foreach ($this->em->getRepository('AcmeDemoBundle:DepartmentType')->findAll() as $type) {
            $choices[$type->getId()] = (string) $type;
        }

        $resolver->setDefaults(array(
            'choices' => $choices,
            'expanded' => true
        ));
    }
}

请注意将经销商传递到DataTransformer以用于转换。


DataTransformer就像这样:

class DepartmentToTypeTransformer implements DataTransformerInterface
{
    private $em;
    private $dealership;

    public function __construct($em)
    {
        $this->em = $em;
    }

    public function transform($department)
    {
        if (null === $department) {
            return $department;
        }

        return $department->getType()->getId();
    }

    public function reverseTransform($type)
    {
        if (null === $type) {
            return $type;
        }

        return $this->em->getRepository('AcmeDemoBundle:Department')->findOneBy(array(
            'dealership' => $this->getDealership(),
            'type' => $type
        ));
    }

    public function getDealership() {
        return $this->dealership;
    }

    public function setDealership($dealership) {
        $this->dealership = $dealership;
        return $this;
    }
}

关于传递给变换器的内容的混淆很可能是由于您绑定的变换器被附加到预先存在的行为,尝试添加true作为addViewTransformer的第二个参数:

    $transformer = new DepartmentToTypeTransformer($this->em);
    $builder->addViewTransformer($transformer, true);

来自the docs

FormBuilder::addViewTransformer(
    DataTransformerInterface $viewTransformer,
    Boolean $forcePrepend = false
)

Appends / prepends a transformer to the view transformer chain.