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?

Florian Eckerstorfer
  • 1,526
  • 1
  • 14
  • 21

2 Answers2

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.

Jojo
  • 2,720
  • 1
  • 17
  • 24
  • 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. – Onema May 08 '13 at 16:36
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.

Vitaly Greck
  • 658
  • 5
  • 9