Symfony2表单设置,未设置关联

时间:2015-08-21 11:23:19

标签: forms symfony doctrine-orm

我有相关的公司和号码实体

/**
 * @var Comapany
 *
 * @ORM\ManyToOne(targetEntity="Company", inversedBy="numbers", cascade={"persist", "remove"})
 * @ORM\JoinColumn(name="company", referencedColumnName="id", nullable=true, onDelete="RESTRICT")
 * @Assert\NotBlank(groups={"client"})
 * @Assert\Valid()
 */
private $company;


/**
 * @var Number[]
 * @ORM\OneToMany(targetEntity="Number", mappedBy="company", fetch="EXTRA_LAZY", cascade={"persist", "remove"})
 * @Assert\Count(min="1")
 */
private $numbers;

我创建了一个用于创建和更新公司实体的表单。此表单应允许为其设置Number实体以及取消设置它们。这就是它的外观呈现方式

enter image description here

这就是它在代码中的样子:

$builder
    ->add('name', 'text', [
        'required' => false
    ])
    ->add('numbers', 'entity', [
        'class' => 'AppBundle:Number',
        'property' => 'number',
        'placeholder' => '',
        'required' => false,
        'multiple' => true,
        'query_builder' => function (EntityRepository $er) use ($builder) {
            if ($builder->getData() && $id = $builder->getData()->getId()) {
                return $er->createQueryBuilder('n')
                    ->where('n.company is NULL')
                    ->orWhere('n.company = :id')
                    ->setParameter('id', $id);
            }
            return $er->createQueryBuilder('n')
                ->where('n.company is NULL');
        }
    ]);

问题是在创建新公司记录时,表单分配了Number实体,但Number实体具有属性" company"它没有被分配,所以没有关系。我通过表单活动解决了这个问题:

$builder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
    foreach ($event->getData()->getNumbers() as $number) {
        $number->setCompany($event->getData());
    }
});

哪个用于创建记录,但是在更新时我有另一个问题,因为我删除了数字关联,我无法访问它们,因此无法在数据库中更新它们。我可以再次选择分配给表单的所有Number实体,然后过滤掉分配给公司的哪些实体,然后手动更新它们,但这感觉很脏,我想以干净的方式解决它。

1 个答案:

答案 0 :(得分:0)

最后找到解决方案,结果证明它有很好的记录: http://symfony.com/doc/current/cookbook/form/form_collections.html

我必须做出的改变是:

1.将by_reference属性添加到实体表单字段:有关示例的更多信息http://symfony.com/doc/current/cookbook/form/form_collections.html#allowing-new-tags-with-the-prototype 基本上我认为没有这个选项symfony2表单使用自己添加关联的方法,并使用此选项设置它调用实体中的方法“addNumber”和“removeNumber”,其中我必须手动添加反向“数字”关联,其中我必须做出第二次改变。

$builder
    ->add('name', 'text', [
        'required' => false
    ])
    ->add('numbers', 'entity', [
        'class' => 'AppBundle:Number',
        'property' => 'number',
        'placeholder' => '',
        'required' => false,
        'multiple' => true,
        'by_reference' => false, //
        'query_builder' => function (EntityRepository $er) use ($builder) {
            if ($builder->getData() && $id = $builder->getData()->getId()) {
                return $er->createQueryBuilder('n')
                    ->where('n.company is NULL')
                    ->orWhere('n.company = :id')
                    ->setParameter('id', $id);
            }
            return $er->createQueryBuilder('n')
                ->where('n.company is NULL');
        }
    ]);

2.我已经通过从拥有(公司实体)方面调用方法setComapany($ this)将Inverse方关联明确设置为拥有方。

/**
 * Add numbers
 *
 * @param \AppBundle\Entity\Number $numbers
 * @return Company
 */
public function addNumber(\AppBundle\Entity\Number $numbers)
{
    $numbers->setCompany($this); //!Important manually set association

    $this->numbers[] = $numbers;

    return $this;
}

这两个更改足以使表单自动添加关联。然而,删除关联还有一点点。

3.更改我必须在控制器操作本身内部正确取消设置关联:我必须在新的ArrayCollection变量中保存当前设置的关联,并在表单验证后手动检查其中的每个项目集合检查表单验证后是否存在。重要的提示: 我还通过调用手动取消设置逆边关联到拥有方: “$号 - > setCompany(空);”

public function editAction(Request $request, Company $company)
{
    $originalNumbers = new ArrayCollection();
    foreach ($company->getNumbers() as $number) {
        $originalNumbers->add($number);
    }

    $form = $this->createForm(new CompanyType(), $company);

    $form->handleRequest($request);

    if ($form->isValid()) {
        $em = $this->getDoctrine()->getManager();

        foreach ($originalNumbers as $number) {
            if (false === $company->getNumbers()->contains($number)) {
                $company->getNumbers()->removeElement($number);
                $number->setCompany(null); //!Important manually unset association
            }
        }

        $em->persist($company);
        $em->flush();

        return $this->redirectToRoute('companies');
    }

    return $this->render('AppBundle:Company:form.html.twig', [
        'form' => $form->createView()
    ]);
}

为了使这种逻辑功能正常运行,所有这些步骤都是必需的,幸运的是,我能够为此提供非常好的文档。

PS。请注意,我打电话

$em->persist($company);
$em->flush();

没有持久化循环内部的每个“Number”实体,在给定的Symfony2文档示例中可以看到,在这种情况下看起来像这样:

$company->getNumbers()->removeElement($number);
$number->setCompany(null);
$em->persist($number);

这是因为我在Entity类中设置了Cascading Relations选项

/**
 * @var Number[]
 * @ORM\OneToMany(targetEntity="Number", mappedBy="company", fetch="EXTRA_LAZY", cascade={"persist", "remove"})
 * @Assert\Count(min="1")
 */
private $numbers;

对于任何挣扎于此的人,我建议彻底阅读整个http://symfony.com/doc/current/cookbook/form/form_collections.html,特别是标有特殊标志的部分✎✚❗☀