Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

symfony2 JMSSerializerBundle deserialize entity with OneToMany association

I have Category OneToMany Post association in doctrine2 setup like this:

Category:

...
/**
 * @ORM\OneToMany(targetEntity="Post", mappedBy="category")
 * @Type("ArrayCollection<Platform\BlogBundle\Entity\Post>")
 */
protected $posts;
...

Post:

...
/**
 * @ORM\ManyToOne(targetEntity="Category", inversedBy="posts")
 * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
 * @Type("Platform\BlogBundle\Entity\Category")
 */
protected $category;
...

I am trying to deserialize following json object (both entities with id of 1 already exist in database)

{
    "id":1,
    "title":"Category 1",
    "posts":[
        {
            "id":1
        }
    ]
}

using JMSSerializerBundle serializer's deserialize method configured with doctrine object constructor

jms_serializer.object_constructor:
    alias: jms_serializer.doctrine_object_constructor
    public: false

with following result:

Platform\BlogBundle\Entity\Category {#2309
  #id: 1
  #title: "Category 1"
  #posts: Doctrine\Common\Collections\ArrayCollection {#2314
    -elements: array:1 [
      0 => Platform\BlogBundle\Entity\Post {#2524
        #id: 1
        #title: "Post 1"
        #content: "post 1 content"
        #category: null
      }
    ]
  }
}

Which is fine at first sight. The problem is, that associated Post has category field set to null, resulting in no association on persist(). If I try to deserialize this:

{
    "id":1,
    "title":"Category 1",
    "posts":[
        {
            "id":1
            "category": {
                "id":1
            }
        }
    ]
}

it works fine but that is not what I want to do :( I suspect that solution could be to somehow reverse the order in which entities are saved. If the post was saved first and category second, this should work.

How to save this association properly?

like image 310
Jiří Brabec Avatar asked Apr 29 '15 09:04

Jiří Brabec


1 Answers

Don't know whether this is still relevant for you but the solution is pretty straight.

You should configure an Accessor with a setter for an association, e.g.:

/**
 * @ORM\OneToMany(targetEntity="Post", mappedBy="category")
 * @Type("ArrayCollection<Platform\BlogBundle\Entity\Post>")
 * @Accessor(setter="setPosts")
 */
protected $posts;

The serializer will call the setter method to populate posts from json. The rest of the logic should be handled inside the setPosts:

public function setPosts($posts = null)
{
    $posts = is_array($posts) ? new ArrayCollection($posts) : $posts;
    // a post is the owning side of an association so you should ensure
    // that its category will be nullified if it's not longer in a collection
    foreach ($this->posts as $post) {
        if (is_null($posts) || !$posts->contains($post) {
            $post->setCategory(null);
        }
    }
    // This is what you need to fix null inside the post.category association
    if (!is_null($posts)) {
        foreach ($posts as $post) {
            $post->setCategory($this);
        }
    }

    $this->posts = $posts;
}
like image 113
origaminal Avatar answered Oct 23 '22 02:10

origaminal