1

Been scratching my head with this one. I have a symfony 2 json api that is providing data to an android app.

The api serializes the data and sends it off to the app using the JMSSerializerBundle with no problems at all. I then use GSON to deserialize to native java objects.

My app then makes changes and posts the data back to the api.

My problem is that when I deserialize the json sent from the app and try to persist/merge it new objects are created in the database for entities which already exist. Can symfony 2 determine whether an object is new or not? I would have thought if it had an Id with the record it would know it was to be updated and not created.

Here's my controller code :

public function postSuggestionAction()
{

    $content = $this->get("request")->getContent();

    if (!empty($content))
    {
        $serializer = $this->get('jms_serializer');
        $suggestion = $serializer->deserialize($content, 'Calling\WebBundle\Entity\Suggestion', 'json');

        $em = $this->getDoctrine()->getManager();
        $em->persist($suggestion);
        $em->merge($suggestion->getCategory());
        $em->merge($suggestion->getNumber());
        $em->merge($suggestion->getCaller());
        $em->flush();

        $view = $this->view(true, 200);
        return $this->handleView($view);

    }

    $view = $this->view(false, 404);
    return $this->handleView($view);



}

Thanks in advance Steve

1 Answers1

1

Update

It sems that EntityManager::merge() method should indeed work. However, you need to have the cascade=MERGE or cascade=ALL option on your entity's mapping. Your code should then become:

public function postSuggestionAction()
{

    $content = $this->get("request")->getContent();

    if (!empty($content))
    {
        $serializer = $this->get('jms_serializer');
        $detachedSggestion = $serializer->deserialize($content, 'Calling\WebBundle\Entity\Suggestion', 'json');

        $em = $this->getDoctrine()->getManager();
        $suggestion = $em->merge($detachedSuggestion);
        $em->flush();

        $view = $this->view(true, 200);
        return $this->handleView($view);

    }

    $view = $this->view(false, 404);
    return $this->handleView($view);



}

Original Answer No, there is no way of doing that, beside querying Doctrine for the entity based on a discriminating (unique) column. The best way would be to have the ID from the JSON, but if you don't, you'll need to have a way to compare your entities to see if they are equal. Then, you can query it from the DB.

You logic would then be:

  • Search for the object in DB (SELECT s FROM Suggestion WHERE s.id = :id ; if you have the id)
  • Return object or null ; if null, keep your logic, otherwise reflect the changes from your JSON to your object, then persist & save.
Hugo Briand
  • 1,683
  • 20
  • 27
  • Thanks for the suggestion. I have the ids so I could do the query but it seems a shame to me that symfony can't recognise an existing object... especially seeing as after deserialization the object is exactly the same as the one I would retrieve from the database. – Stephen Reid Apr 04 '13 at 16:11
  • I should also mention that the Suggestion is a new object but it's children ( foreign key references ) are also passed back in the json and they are the ones that already exist. – Stephen Reid Apr 04 '13 at 16:15
  • That's why you have to retrieve the object from the DB, and edit this one, when you need to update it. The same logic may be used for the children. – Hugo Briand Apr 04 '13 at 16:41
  • I found this http://stackoverflow.com/questions/11213411/how-to-manage-deserialized-entities-with-entity-manager that seems to suggest that it is possible but I still can't get it working. – Stephen Reid Apr 04 '13 at 19:59
  • Indeed, from what I read, merge performs the logic explained in the answer. In order for it to work in related entities, you have to loop over those entities (and merge them) prior to merging the "containing" entity. I'm updating the answer to reflect that. – Hugo Briand Apr 05 '13 at 14:03
  • Instead of looping, you can also use cascade=MERGE, which seems a better option :) – Hugo Briand Apr 05 '13 at 14:09