EasyAdminBundle: ManyToMany relation is not saved to database

I can add category in the post but can’t add post in the category.

Post Entity:

<?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * Post
 *
 * @ORM\Table(name="post")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\PostRepository")
 */
class Post
{
    const NUM_ITEMS = 10;

    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

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

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

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

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="text")
     */
    private $content;

    /**
     * @var \DateTime
     *
     * @ORM\Column(name="publishedAt", type="datetime")
     */
    private $publishedAt;

    /**
     * @ORM\ManyToMany(targetEntity="Category", inversedBy="posts", cascade={"persist"})
     * @ORM\JoinTable(name="post_category")
     */
    private $categories;

    public function __toString()
    {
        return $this->title;
    }

    /**
     * Post constructor.
     */
    public function __construct()
    {
        $this->publishedAt = new \DateTime();
        $this->categories = new ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set title
     *
     * @param string $title
     * @return Post
     */
    public function setTitle($title)
    {
        $this->title = $title;

        return $this;
    }

    /**
     * Get title
     *
     * @return string 
     */
    public function getTitle()
    {
        return $this->title;
    }

    /**
     * Set slug
     *
     * @param string $slug
     * @return Post
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;

        return $this;
    }

    /**
     * Get slug
     *
     * @return string 
     */
    public function getSlug()
    {
        return $this->slug;
    }

    /**
     * Set summary
     *
     * @param string $summary
     * @return Post
     */
    public function setSummary($summary)
    {
        $this->summary = $summary;

        return $this;
    }

    /**
     * Get summary
     *
     * @return string 
     */
    public function getSummary()
    {
        return $this->summary;
    }

    /**
     * Set content
     *
     * @param string $content
     * @return Post
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * Get content
     *
     * @return string 
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * Set publishedAt
     *
     * @param \DateTime $publishedAt
     * @return Post
     */
    public function setPublishedAt($publishedAt)
    {
        $this->publishedAt = $publishedAt;

        return $this;
    }

    /**
     * Get publishedAt
     *
     * @return \DateTime 
     */
    public function getPublishedAt()
    {
        return $this->publishedAt;
    }

    /**
     * Add categories
     *
     * @param \AppBundle\Entity\Category $categories
     * @return Post
     */
    public function addCategory(\AppBundle\Entity\Category $categories)
    {
        $this->categories[] = $categories;

        return $this;
    }

    /**
     * Remove categories
     *
     * @param \AppBundle\Entity\Category $categories
     */
    public function removeCategory(\AppBundle\Entity\Category $categories)
    {
        $this->categories->removeElement($categories);
    }

    /**
     * Get categories
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getCategories()
    {
        return $this->categories;
    }
}

Category Entity:

 <?php

namespace AppBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * Category
 *
 * @ORM\Table(name="category")
 * @ORM\Entity(repositoryClass="AppBundle\Repository\CategoryRepository")
 */
class Category
{
    /**
     * @var int
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

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

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

    /**
     * @var string
     *
     * @ORM\Column(name="content", type="text")
     */
    private $content;

    /**
     * @ORM\ManyToMany(targetEntity="Post", mappedBy="categories", cascade={"persist"})
     */
    private $posts;

    public function __toString()
    {
        return $this->name;
    }

    /**
     * Constructor
     */
    public function __construct()
    {
        $this->posts = new ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Category
     */
    public function setName($name)
    {
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set slug
     *
     * @param string $slug
     * @return Category
     */
    public function setSlug($slug)
    {
        $this->slug = $slug;

        return $this;
    }

    /**
     * Get slug
     *
     * @return string 
     */
    public function getSlug()
    {
        return $this->slug;
    }

    /**
     * Set content
     *
     * @param string $content
     * @return Category
     */
    public function setContent($content)
    {
        $this->content = $content;

        return $this;
    }

    /**
     * Get content
     *
     * @return string 
     */
    public function getContent()
    {
        return $this->content;
    }

    /**
     * Add posts
     *
     * @param \AppBundle\Entity\Post $posts
     * @return Category
     */
    public function addPost(\AppBundle\Entity\Post $posts)
    {
        $this->posts[] = $posts;

        return $this;
    }

    /**
     * Remove posts
     *
     * @param \AppBundle\Entity\Post $posts
     */
    public function removePost(\AppBundle\Entity\Post $posts)
    {
        $this->posts->removeElement($posts);
    }

    /**
     * Get posts
     *
     * @return \Doctrine\Common\Collections\Collection 
     */
    public function getPosts()
    {
        return $this->posts;
    }
}

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Comments: 19 (14 by maintainers)

Most upvoted comments

You need to set by_reference option to false in collection form in Category:

- { property: 'posts', type_options: { by_reference: false} }

and also as @Pierstoval said modify Category entity:

// Category.php

    /**
     * Add posts
     *
     * @param \AppBundle\Entity\Post $posts
     * @return Category
     */
    public function addPost(\AppBundle\Entity\Post $posts)
    {
        if (!$this->posts->contains($posts)) {
            $this->posts[] = $posts;
            $posts->addCategory($this);
        }

        return $this;
    }

    /**
     * Remove posts
     *
     * @param \AppBundle\Entity\Post $posts
     */
    public function removePost(\AppBundle\Entity\Post $posts)
    {
        $this->posts->removeElement($posts);
        $posts->removeCategory($this);
    }

this is said in documentation http://symfony.com/doc/current/cookbook/form/form_collections.html

A second potential issue deals with the Owning Side and Inverse Side of Doctrine relationships. In this example, if the “owning” side of the relationship is “Task”, then persistence will work fine as the tags are properly added to the Task. However, if the owning side is on “Tag”, then you’ll need to do a little bit more work to ensure that the correct side of the relationship is modified.

The trick is to make sure that the single “Task” is set on each “Tag”. One easy way to do this is to add some extra logic to addTag(), which is called by the form type since by_reference is set to false:

I seem to have solved a similar issue that I was having by:

  • Making sure the inversedBy and mappedBy is set up correctly
  • the addCollection() method was present in both Entities (this needs to reflect the field name, e.g. addUser, addCategory etc…)
  • the field is declared in EasyAdmin Controller using the AssociationField
  • the field in the Controller has the by_reference property set to false using setFormTypeOptionIfNotSet('by_reference', false)

This last point is only needed if trying to persist changes when working on an Entity that is the reverse side.

    public function configureFields(string $pageName) : iterable
    {
        return [
            IdField::new('id')
                ->hideOnForm(),
            AssociationField::new('categories')
                ->setFormTypeOptionIfNotSet('by_reference', false)
                ->hideOnIndex(),
        ];
    }
    ````

I’m pretty sure this has nothing to do with EasyAdmin itself but the Form component + Doctrine. As my wife gave birth a few days ago I don’t have time to “code” to test this, I can just advice you to take a look outside EasyAdmin issue tracker (on StackOverflow for instance) to check what’s going on. If you find a solution it could be good for you to say it back here 😉

Thought I would just add this here, as I was curious about the by_reference type option:

Similarly, if you’re using the CollectionType field where your underlying collection data is an object (like with Doctrine’s ArrayCollection), then by_reference must be set to false if you need the adder and remover (e.g. addAuthor() and removeAuthor()) to be called.

http://symfony.com/doc/current/reference/forms/types/collection.html#by-reference

I can confirm this before are working and now no

Edit: I am getting this problem with a ManyToOne

So, this occurs always from a select2 multiple