Symfony + Forms, by_reference = true и allow_add в полях CollectionType - PullRequest
0 голосов
/ 11 ноября 2019

Я относительно новичок в Symfony, так что я мог бы что-то неправильно понять. Я запутался, когда захотел бы установить by_reference = true в поле формы CollectionType с allow_add = true.

Для контекста: я попал в этот беспорядок, когда пытался использовать прослушиватель событий доктриныпрослушивать preRemove на объекте, который удаляется путем удаления его из поля формы CollectionType с помощью allow_delete => true, чтобы инициировать обновление родительского объекта. С by_reference => false в классе формы я не могу получить доступ к родительской сущности внутри обработчика preRemove, но вызывается родительский метод removeEntity (). С by_reference => true я могу получить доступ к родителю из сущности, подлежащей удалению, но его метод removeEntity () не вызывается. Это имеет смысл для меня, моя проблема с добавлением дочерних сущностей с формой.

Упрощенно, у меня есть две сущности:

// SimpleParent.php
/**
* @ORM\Entity(repositoryClass="App\Repository\SimpleParentRepository")
*/
class SimpleParent
{
    /**
    * @ORM\Id()
    * @ORM\GeneratedValue()
    * @ORM\Column(type="integer")
    */
    private $id;

    /**
    * @ORM\OneToMany(targetEntity="App\Entity\ChildEntity", mappedBy="parent", orphanRemoval=true, cascade={"persist", "remove"})
    */
    private $childEntities;

    public function addChildEntity(ChildEntity $childEntity): self
    {
        if (!$this->childEntities->contains($childEntity)) {
            $this->childEntities[] = $childEntity;
            $childEntity->setParent($this);
        }

        return $this;
    }

    // other getter, adder, remover methods as autogenerated, left out for brevity
}

И

// ChildEntity.php
/**
* @ORM\Entity(repositoryClass="App\Repository\ChildEntityRepository")
*/
class ChildEntity
{
    /**
    * @ORM\Id()
    * @ORM\GeneratedValue()
    * @ORM\Column(type="integer")
    */
    private $id;

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

    /**
    * @ORM\ManyToOne(targetEntity="App\Entity\SimpleParent", inversedBy="childEntities")
    * @ORM\JoinColumn(nullable=false)
    */
    private $parent;

    // getter, adder, remover methods as autogenerated, left out for brevity
}

Сдва очень простых класса форм:

class SimpleParentType extends AbstractType
{

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

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('childEntities', NiceRepeaterType::class, [
            "entry_type" => ChildEntityType::class,
            "allow_delete" => true,
            "by_reference" => true,
            "allow_add" => true,
        ]);
    }
}

class ChildEntityType extends AbstractType
{
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => ChildEntity::class,
        ]);
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {

        $builder->add("name", TextType::class, ["required" => false]);

    }
}

и очень простой код в контроллере (для простоты, я просто сбрасываю здесь entityManager)

$form = $this->createForm(SimpleParentType::class, $simpleParent);
$form->handleRequest($request);
if($form->isSubmitted() && $form->isValid()) {
    $simpleParent = $form->getData();
    $this->entityManager->flush();
}

Классы use правильно, просто опущено для краткости. Используя прототип, я добавляю новую строку. Тем не менее, отправка формы вызывает исключение:

An exception occurred while executing 'INSERT INTO child_entity (name, parent_id) VALUES (?, ?)' with params ["aaa", null]: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'parent_id' cannot be null

Если я сделаю столбец соединения для ChildEntity обнуляемым, ChildEntity вставляется, но не имеет связи с SimpleParent, потому что установлен parent_idк нулю. Нет необходимости говорить, что эти вставленные строки ChildEntity не отображаются в форме в коллекции. Это имеет смысл, потому что сумматор родителя никогда не вызывается, и поэтому он никогда не делает setParent($this) для нового ChildEntity.

С by_reference => false он работает безошибочно, как и ожидалось, ChildEntity создается и передается в SimpleParent:: addChildEntity, который устанавливает родителя ChildEntity. Когда менеджер сущностей сбрасывается, строка ChildEntity вставляется с установленным для parent_id идентификатором SimpleParent.

У меня такой вопрос: почему у меня когда-либо было бы by_reference => true в CollectionType, если я также хочу allow_add => true? Я заметил, что объект, который я получаю из $form->getData() в контроллере, содержит коллекцию, включая ChildEntity, который в настоящее время не имеет родителя. Я мог бы сделать это вручную с помощью

$simpleParent = $form->getData();
foreach($simpleParent->getChildEntities() as $child) {
    $child->setParent($simpleParent);
}

Но какова причина такого подхода? Я чувствую, что упускаю что-то очевидное. Любой совет приветствуется.

...