3

鉴于以下情况,我应该将逻辑绑定到 Review 的位置:

实体:

  • 经销商(有很多部门)
  • 部门(有一种)
  • 部门类型
  • 审查(有一个经销商和一个部门)

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

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

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


到目前为止,我尝试了两种方法,但都遇到了死胡同/混乱。

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

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

4

2 回答 2

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)
{
    ...
}
于 2012-07-25T17:36:48.757 回答
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
        ));
    }
}

请注意将 Dealership 传递到 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);

文档

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

Appends / prepends a transformer to the view transformer chain.
于 2012-07-26T12:41:36.927 回答