解决了Symfony 4,对象和子对象,缺少外键

时间:2019-05-02 19:32:42

标签: forms symfony

编辑并解决+更新

am 找不到找到以下内容的窍门。

说我有两个df.Number = df.Number.astype(int) EntityMainMinor一对多MainMinor是外键领域。

我希望同时具有(次要)形式来创建mainId对象,以便用户可以从已经可用的Minor对象列表中选择其Main对象,以及(主要)形式来一次创建Main对象和可能同时创建许多不同的Main(子)对象的表单。

问题是在后一种情况下,我 am 无法保存外键。

Minor类中,我拥有Main属性

Minors

/** * @ORM\OneToMany(targetEntity="App\Entity\Minor", mappedBy="Main", cascade={"persist"}) */ private $Minors; NOTE: cascade={"persist"} HELPS!!!!

setter/getter

对于次要表格,我定义:

 /**
 * @return Collection|Minor[]
 */
public function getMinors(): Collection
  {
    return $this->Minors;
  }

public function addMinor(Minor $minor): self
  {
    if (!$this->Minors->contains($minor)) {
        $this->Minors[] = $minor;
        $minor->setMain($this);   <<<  THIS HELPS!!!
    }

    return $this;
  }

public function removeMinor(Minor $minor): self
  {
    if ($this->Minors->contains($minor)) {
        $this->Minors->removeElement($minor);
        // set the owning side to null (unless already changed)
        if ($minor->getMain() === $this) {
            $minor->setMain(null);
        }
    }

    return $this;
  }

对于主表单,我尝试过:

....
$builder ->add('minorTitle')
         ->add('Main', EntityType::class, array(
               'class' => Main::class,
               'choice_label' => 'mainTtile',
               'label' => 'main'));
    if(empty($options['remove_main_field'])) {
        // field is the same, but isn't added always, due to 'if'
        $builder->add('Main', EntityType::class, array(
        'class' => Main::class,
        'choice_label' => 'mainTitle',
        'label' => 'Main',
    ));
} 
.....
$resolver->setDefaults([
        'data_class' => Minor::class,
        'remove_main_field' => false, <<< THIS HELPS!!!
    ]);

因此,次要表单确实是作为主表单中的子表单嵌入的。要添加更多子表单,我有CollectionType中建议的一些JS。感谢.... $builder ->add('mainTitle') ->add('Minors', CollectionType::class, array( 'entry_type' => MinorType::class, 'entry_options' => ['label' => true, 'remove_main_field' => true], <<< THIS HELPS!!! 'allow_add' => true, 'label' => 'Minor' )) ..... 'data_class' => Main::class` ,不再需要以下技巧。 为了避免在次要子窗体中显示remove_main_field字段,我对Main进行了一些修改,例如:

prototype

用户可以创建一个Main对象,也可以创建许多次要对象,但是前一个/* newWidget = newWidget.replace(newWidget.match(/\B<div class="form-group"><label class="required" for="main_Minors___name_Main">Main<\/label><select id="main_Minors___name_Main" name="main\[Minors\]\[__name__\]\[Main\]" class="form-control">.*<\/select>\B/g),""); */ 被保存为作为后者的外键。最终,我得到了有效的代码:

id

已解决编辑问题!!我不需要如下定义public function new(Request $request): Response { $em = $this->getDoctrine()->getManager(); $main = new Main(); $form = $this->createForm(MainType::class, $main); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) {

postData

,但要么不起作用,要么保存两次相同的子对象(只有一次使用正确的外键保存)。

非常感谢Jakumi!

1 个答案:

答案 0 :(得分:2)

只有一些提示。

  1. 您的表单类型应将data_class 选项设置为适当的类。
  2. 您的表单字段名称应与实体上的属性名称匹配。 (默认情况下,所有属性名称都在camelCase中,小写的第一个字符...在symfony中)
  3. 在1.和2.之后,您只需调用$form->getData()就可以得到适当的实体(或者,您可能已经注意到,当您给createForm调用实体时,该实体将通过以下形式进行修改:组件-可能并非总是有意的。请在不需要时考虑使用数据传输对象(DTO)。
  4. 您的CollectionType字段应将选项 byReference设置为false,以便在设置字段上使用设置者(Main::setMinors,在这种情况下)。
  5. 通常一对多的一面(即Main类)可以摆脱:

    public function setMinors(array $minors) {
        foreach($minors as $minor) {
            $minor->setMain($this); // set the main, just to be safe
        }
        $this->minors = $minors;    // set the property Main.minors
    }
    

    但是您也应该也不相反地在setMain中执行此操作(这也不是那么简单。setMinors的替代方法是addMinor和{{1} },这两种解决方案都有其优点和成本,但是当涉及到形式时,我认为它们是相当的。

  6. removeMinor上的
  7. 如果您在Main(即cascade={"PERSIST"})上设置了OneToMany选项,则不必显式地对所有未成年人呼叫持久化,则只要您持久(并刷新)@ORM\OneToMany(targetEntity="App\Entity\Minor", cascade={"PERSIST"})对象/实例,它们就会持久化。

  8. 最后,或者为您的次要类型添加一个选项,以忽略Main表单字段,或者添加一个没有{{1}的新表单类型main(或其他任何内容) }表单字段(扩展MainMinorType并删除main字段)。这消除了肮脏的黑客的必要; o)

但是,总的来说,如果您没有以双向关系将未成年人设置在主体上,则结果将无法明确定义。 (假设一会儿,A有一个到B的链接,但是B没有到A的链接,但是应该有一个链接,因为它是双向关系。这可能意味着必须建立链接。它也可能意味着应该删除该链接。因此,为了安全起见并清楚地传达意图,请设置双方!)最终,这可能是其无法按预期工作的原因。

更新

要详细说明第7点,您的MinorType可以这样修改:

main

在您的MinorType中,以下项已添加了新选项

class MinorType extends AbstractType {
    public function buildForm(FormBuilderInterface $builder, array $options) {
        // ... other fields before
        if(empty($options['remove_main_field'])) {
            // field is the same, but isn't added always, due to 'if'
            $builder->add('main', EntityType::class, [
               'class' => Main::class,
               'choice_label' => 'mainTtile',
               'label' => 'main'
            ]);
        }
        // ... rest of form
    }

    public function configureOptions(OptionsResolver $resolver) {
        // maybe parent call ...
        $resolver->setDefaults([
            // your other defaults
            'remove_main_field' => false, // add new option, self-explanatory
        ]);
    }
}

现在,当主表单中的 embedded 时,这将从未成年人表单中删除该主字段。但是默认设置是不删除主字段,因此当您自己编辑次要对象时,将显示主字段,就像以前一样……除非我在代码中输入错误;否则)