具有自引用数据的表单 (Symfony 5)

Form with self-referencing data (Symfony 5)

我正在用 Symfony 5 做一个应用程序,但有一个问题我找不到解决方案,我不知道。

我想制作一个实体“人”的表格。 一个人可以在他的家庭中添加另一个人。

所以在我的实体中我做了一个多对多的自引用到人。

class Person
{
    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type="integer")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=50)
     */
    private $name;

    /**
     * @ORM\Column(type="string", length=50)
     */
    private $firstname;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $birthdaydate;

    /**
     * @ORM\Column(type="string", length=255)
     */
    private $gender;

    /** 
     * @ManyToMany(targetEntity="Person")
     * @JoinTable(name="family",
     *      joinColumns={@JoinColumn(name="person__id", referencedColumnName="person__id")},
     *      inverseJoinColumns={@JoinColumn(name="family_id", referencedColumnName="person__id")}
     *      )
     */
    private $myFamily;

现在,我想创建一个表单,我可以在其中添加新的 Person,in a person。 我做了一个 CollectionType,就像 symfony 说的,但是当我想把它打印到页面上时,由于无限循环,我超时了。

导致问题的是“allow_add”。

我需要“allow_add”返回的原型变量在前面添加新字段。

class PersonType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class,  ['attr' => ['class' => 'form_textfield']])
            ->add('firstname')
            ->add('birthdayDate', TextType::class,  ['attr' => ['class' => 'form_datetime']])
            ->add('gender', GenderType::class)
            ->add('submit', SubmitType::class)
            ->add('myFamily', CollectionType::class, array('entry_type' => PersonType::class, 'mapped' => false, 'allow_add' => true, 'by_reference' => false, 'allow_delete' => true));
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Person::class,
        ]);
    }
}

这是我的表格,但没有什么有趣的,我会在解决这个问题时添加必要的 js。

{% extends 'base.html.twig' %}

{% block title %}Hello PersonController!
{% endblock %}

{% block body %}
    {{ form_start(form) }}
    {{ form_row(form.name) }}
    {{ form_row(form.firstname) }}
    {{ form_row(form.birthdayDate) }}
    {{ form_row(form.gender) }}
{{ form_row(form.myFamily) }}
    <button type="button" class="add_item_link" data-collection-holder-class="tags">Add a tag</but
    {{ form_end(form) }}
{% endblock %}

先谢谢大家了。

存在无限循环,因为 myFamily 属性 引用了一个 Person 实体,该实体本身引用了 myFamily 属性 ...

为简单起见,管理一个人的家庭的一种方法是创建一个单独的家庭实体。 从 Person 的角度来看,与家庭建立 ManyToOne 关系似乎更连贯。 之后,您可以使用 PersonFormType 中的 EntityType:class 添加 Person 的家庭。

这是 EntityType 的文档:https://symfony.com/doc/current/reference/forms/types/entity.html

Dylan Kas 的回答很好,就是加了一个新的表格就很好了。

人形

class PersonType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class,  ['attr' => ['class' => 'form_textfield']])
            ->add('firstname')
            ->add('birthdayDate', TextType::class,  ['attr' => ['class' => 'form_datetime']])
            ->add('gender', GenderType::class)
            ->add('submit', SubmitType::class)
            ->add('myFamily', CollectionType::class, array('entry_type' => ChildType::class, 'by_reference' => false, 'allow_add' => true, 'allow_delete' => true));
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Person::class,
        ]);
    }
}

child,被 myFamily 引用:

class ChildType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class,  ['attr' => ['class' => 'form_textfield']])
            ->add('firstname')
            ->add('birthdayDate', TextType::class,  ['attr' => ['class' => 'form_datetime']])
            ->add('gender', GenderType::class)
            ->add('submit', SubmitType::class);
    }

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => Person::class,
        ]);
    }
}

和视图:

{% block body %}
    {{ form_start(form) }}
    {{ form_row(form.name) }}
    {{ form_row(form.firstname) }}
    {{ form_row(form.birthdayDate) }}
    {{ form_row(form.gender) }}
    <button type="button" class="add_item_link" data-collection-holder-class="myFamily">Add a tag</button>
    <ul class="myFamily" data-index="{{ form.myFamily|length > 0 ? form.myFamily|last.vars.name + 1 : 0 }}" data-prototype="{{ form_widget(form.myFamily.vars.prototype)|e('html_attr') }}"></ul>
    {{ form_end(form) }}
{% endblock %}

与js关联

const addFormToCollection = (e) => {
  const collectionHolder = document.querySelector(
    "." + e.currentTarget.dataset.collectionHolderClass
  );

  const item = document.createElement("li");

  item.innerHTML = collectionHolder.dataset.prototype.replace(
    /__name__/g,
    collectionHolder.dataset.index
  );

  collectionHolder.appendChild(item);

  collectionHolder.dataset.index++;
};

document
  .querySelectorAll(".add_item_link")
  .forEach((btn) => btn.addEventListener("click", addFormToCollection));

它仍然需要一些工作,也许我可以使 child 表单扩展 person 表单。前面也需要一些工作。但是下一个遇到这个问题的人将在这里找到解决方案。

我还在问自己,如果我需要一个包含相同表单的表单,包含相同表单的表单,我该怎么办... 该表格将是可递归的。