Quantcast
Channel: Active questions tagged symfony4 - Stack Overflow
Viewing all articles
Browse latest Browse all 3925

Symfony + Forms, by_reference = true and allow_add in CollectionType fields

$
0
0

I'm relatively new to Symfony, so I might grossly misunderstand something. I'm confused about when I'd want to set by_reference = true on a CollectionType form field with allow_add = true.

For context: I got myself into this mess when I was trying to use a doctrine event listener to listen to preRemove on an entity that is removed by removing it from a CollectionType form field with allow_delete => true in order to trigger an update on the parent entity. With by_reference => false on the form class, I cannot access the parent entity inside the preRemove handler but the parent's removeEntity() method gets called. With by_reference => true, I can access the parent from the to-be-deleted entity, but its removeEntity() method does not get called. That makes sense to me, my trouble is with adding child entities with a form.

Simplified, I have two entities:

// 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
}

And

// 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
}

With two very simple form classes:

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]);

    }
}

and a very simple code in the controller (for simplicity's sake, I'm just flushing the entityManager here)

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

Classes are used correctly, just left out for brevity. Using the prototype, I add a new row. However, submitting the form throws an exception:

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

If I make the join column on the ChildEntity nullable, the ChildEntity gets inserted but has no connection to the SimpleParent because parent_id is set to null. Needless to say that these inserted ChildEntity rows don't show up in the form in the Collection. This makes sense because the parent's adder never gets called and so it never does setParent($this) on the new ChildEntity.

With by_reference => false, it works flawlessly as expected, the ChildEntity is created and is passed to SimpleParent::addChildEntity which sets the ChildEntity's parent. When the entity manager is flushed, the ChildEntity row is inserted with the parent_id set to the id of SimpleParent.

My question is: why would I ever have by_reference => true on a CollectionType if I also want allow_add => true? I've noticed that the object I get from $form->getData() in the controller contains the Collection including the ChildEntity that is currently without a parent. I could do that manually with

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

But what reason is there for this approach? I feel like I'm missing something obvious. Any advice is appreciated.


Viewing all articles
Browse latest Browse all 3925

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>