How do I update embedded documents in Doctrine MongoDB?
Asked Answered
T

2

6

I have a document that has a document embedded. When I create an object for the first time everything works fine, but when I try to update a document, the embedded document does not get updated.

/** @MongoDB\Document */
class DocumentA
{
    /** @MongoDB\EmbedOne(targetDocument="DocumentB") **/
    protected $docB;

    /** @MongoDB\String */
    protected $valueA;
}

/** @MongoDB\EmbeddedDocument */
class DocumentB
{
    /** @MongoDB\String */
    protected $valueB;
}

In my application I query for a document, update the values and persist them to the data store.

// Variant A – Does not work
$document = ... // find from data store
$document->setValueA('Hello World');
$document->getDocB()->setValueB('foo baz');

$om->persist($document);
$om->flush();

If I do not update the embedded document, but set a new one everything works fine:

// Variant B - Does work
$document = ... // find from data store
$document->setValueB('Hello World 2');
$document->setDocB(new DocumentB());
$document->getDocB()->setValueB('foo baz 2');

$om->persist($document);
$om->flush();

As I said, Variant B works fine. However, in my application the documents are more complex and it would be impractical for me to create a new object for the embedded document every time I have to update it. Is there a method to tell Doctrine ODM to look at the values of an embedded document to decide if it should be updated?

Tarlatan answered 5/9, 2012 at 16:20 Comment(0)
U
2

I faced exactly the same issue. It turns out the UnitOfWork seems to fail in computing the changesets of documents with other documents embedded, though I have not been able to figure out why... As a result, when when i compare the actual value and the original one, unit of work shows the same value for both. Speaking with your Variant A, when you

$document->getDocB()->setValueB('foo baz');

Unit of work shows "foo baz" for both the old and the new value and will not recognize it as a a change and will therefor not update it.

Anyway, this leeds to a workaround:

$document = ... // find from data store
$document->setValueA('Hello World');
$docB = $document->getDocB();
$docB->setValueB('foo baz');
$om->detach($docB);
$om->persist($document);
$om->flush();

This makes the unit of work recognize the docB of $document as a newly set document and will flush it as expected.

Unsung answered 20/3, 2013 at 13:29 Comment(1)
I was having issues replacing an embedded document with a different one. In my case DocumentB is not an EmbeddedDocument but rather a Document. Using detach on the object like you showed here worked in this scenario as well.Bedel
C
0

MongoDB has only atomic operations. You have options: 1. Query the document, find the apropriate subdocument, update the whole document or its part. Pros: easy logic Cons: non-atomic 2. Use positional $ operator is your subdocuments are in list.

Confucian answered 4/2, 2013 at 11:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.