Home > Back-end >  EntityType multiselect for ManyToMany does not remove relations if deselect an option
EntityType multiselect for ManyToMany does not remove relations if deselect an option

Time:08-13

Having a ManyToMany relation between Article and Tag i have a form with a multiselect of type EntityType using Symfony FormBuilder.

Everything seems fine, even preselection, except when deselecting an already related Tag from the tags-select, it does not remove the underlying relation. Therefor I can only add more relations.

I tried removing all relations before saving but because of LazyLoading I get other trouble there. I cannot find anything regarding this in the documentation but this might be very common basics, right?

Can anyone help me out here?

This is the code of my EntityType-Field

$builder->add('tags', FormEntityType::class, [
    'label'        => 'Tags',
    'required'     => false,
    'multiple'     => true,
    'expanded'     => false,
    'class'        => Tag::class,
    'choice_label' => function (Tag $tag) {
        return $tag->getName();
    },
    'query_builder' => function (TagRepository $er) {
        return $er->createQueryBuilder('t')
            ->where('t.isActive = true')
            ->orderBy('t.sort', 'ASC')
            ->addOrderBy('t.name', 'ASC');
    },
    'choice_value' => function(?Tag $entity) {
        return $tag ? $tag->getId() : '';
    },
    'choice_attr'  => function ($choice) use ($options) {
        // Pre-Selection
        if ($options['data']->getTags() instanceof Collection
            && $choice instanceof Tag) {
            if ($options['data']->getTags()->contains($choice)) {
                return ['selected' => 'selected'];
            }
        }
        return [];
    }
]);

And this is how I save after form-submit

$articleForm = $this->createForm(ArticleEditFormType::class, $article)->handleRequest($this->getRequest());

    if ($articleForm->isSubmitted() && $articleForm->isValid()) {
        // saving the article
        $article = $articleForm->getData();
        $this->em->persist($article);
        $this->em->flush();

    }

The Relations look like this:

Article.php

    /**
     * @ORM\ManyToMany(targetEntity=Tag::class, mappedBy="articles", cascade={"remove"})
     * @ORM\OrderBy({"sort" = "ASC"})
     */
    private $tags;


    public function removeTag(Tag $tag): self
    {
        if ($this->tags->removeElement($tag)) {
            $tag->removeArticle($this);
        }

        return $this;
    }

Tag.php


    /**
     * @ORM\ManyToMany(targetEntity=Article::class, inversedBy="tags", cascade={"remove"})
     * @ORM\OrderBy({"isActive" = "DESC","name" = "ASC"})
     */
    private $articles;

    public function removeArticle(Article $article): self
    {
        $this->articles->removeElement($article);

        return $this;
    }

CodePudding user response:

Setters, adders, etc. won't be called on the entity by default.

In the case of a multiselect where the underlying object is a collection, to ensure that the element is removed by calling removeArticle you have to set the option by_reference to false.

This is applicable to CollectionType as well.

  • Related