Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[MODM-52] Fixing issue with computing changesets of nested embedded d…
…ocuments that are multiple levels deep
  • Loading branch information
jwage committed Aug 18, 2010
1 parent 485fbdf commit 3e71c6a
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 37 deletions.
13 changes: 11 additions & 2 deletions lib/Doctrine/ODM/MongoDB/Persisters/BasicDocumentPersister.php
Expand Up @@ -207,6 +207,7 @@ public function update($document, array $options = array())
{
$id = $this->uow->getDocumentIdentifier($document);
$update = $this->prepareUpdateData($document);

if ( ! empty($update)) {
if ($this->dm->getEventManager()->hasListeners(ODMEvents::onUpdatePrepared)) {
$this->dm->getEventManager()->dispatchEvent(
Expand Down Expand Up @@ -418,6 +419,9 @@ public function prepareInsertData($document)
*/
public function prepareUpdateData($document)
{
if (is_array($document) && isset($document['originalObject'])) {
$document = $document['originalObject'];
}
$oid = spl_object_hash($document);
$class = $this->dm->getClassMetadata(get_class($document));
$changeset = $this->uow->getDocumentChangeSet($document);
Expand Down Expand Up @@ -447,8 +451,13 @@ public function prepareUpdateData($document)
if ($old !== $new) {
$old = $old ? $old : array();
$new = $new ? $new : array();
$deleteDiff = array_udiff_assoc($old, $new, function($a, $b) {return $a === $b ? 0 : 1; });
$insertDiff = array_udiff_assoc($new, $old, function($a, $b) {return $a === $b ? 0 : 1;});
$compare = function($a, $b) {
$a = is_array($a) && isset($a['originalObject']) ? $a['originalObject'] : $a;
$b = is_array($b) && isset($b['originalObject']) ? $b['originalObject'] : $b;
return $a === $b ? 0 : 1;
};
$deleteDiff = array_udiff_assoc($old, $new, $compare);
$insertDiff = array_udiff_assoc($new, $old, $compare);

// insert diff
if ($insertDiff) {
Expand Down
88 changes: 53 additions & 35 deletions lib/Doctrine/ODM/MongoDB/UnitOfWork.php
Expand Up @@ -323,6 +323,58 @@ public function getDocumentChangeSet($document)
return array();
}

public function getDocumentActualData($document)
{
$class = $this->dm->getClassMetadata(get_class($document));
$actualData = array();
foreach ($class->reflFields as $name => $refProp) {
$mapping = $class->fieldMappings[$name];

// Skip identifiers if custom ones are not allowed
if ($class->isIdentifier($name) && ! $class->getAllowCustomID()) {
continue;
}

$origValue = $class->getFieldValue($document, $mapping['fieldName']);

if (($class->isCollectionValuedReference($name) || $class->isCollectionValuedEmbed($name))
&& $origValue !== null && ! ($origValue instanceof PersistentCollection)) {
// If $actualData[$name] is not a Collection then use an ArrayCollection.
if ( ! $origValue instanceof Collection) {
$value = new ArrayCollection($origValue);
} else {
$value = $origValue;
}

// Inject PersistentCollection
if ($class->isCollectionValuedReference($name)) {
$coll = new PersistentCollection($value, $this->dm);
} else {
$coll = new PersistentCollection($value);
}
$coll->setOwner($document, $mapping);
$coll->setDirty( ! $coll->isEmpty());
$class->reflFields[$name]->setValue($document, $coll);
}

if ($class->isCollectionValuedEmbed($name) && $origValue) {
$embeddedDocuments = $origValue;
$actualData[$name] = array();
foreach ($embeddedDocuments as $key => $embeddedDocument) {
$actualData[$name][$key] = $this->getDocumentActualData($embeddedDocument);
$actualData[$name][$key]['originalObject'] = $embeddedDocument;
}
} elseif ($class->isSingleValuedEmbed($name) && is_object($origValue)) {
$embeddedDocument = $origValue;
$actualData[$name] = $this->getDocumentActualData($embeddedDocument);
$actualData[$name]['originalObject'] = $embeddedDocument;
} else {
$actualData[$name] = $origValue;
}
}
return $actualData;
}

/**
* Computes the changes that happened to a single document.
*
Expand Down Expand Up @@ -355,41 +407,7 @@ public function computeChangeSet($parentDocument, Mapping\ClassMetadata $class,
}

$oid = spl_object_hash($document);
$parentOid = spl_object_hash($parentDocument);

$actualData = array();
foreach ($class->reflFields as $name => $refProp) {
$mapping = $class->fieldMappings[$name];
if ( ! $class->isIdentifier($name) || $class->getAllowCustomID()) {
$actualData[$name] = $class->getFieldValue($document, $mapping['fieldName']);
}
if (($class->isCollectionValuedReference($name) || $class->isCollectionValuedEmbed($name))
&& $actualData[$name] !== null && ! ($actualData[$name] instanceof PersistentCollection)) {
// If $actualData[$name] is not a Collection then use an ArrayCollection.
if ( ! $actualData[$name] instanceof Collection) {
$actualData[$name] = new ArrayCollection($actualData[$name]);
}

// Inject PersistentCollection
if ($class->isCollectionValuedReference($name)) {
$coll = new PersistentCollection($actualData[$name], $this->dm);
} else {
$coll = new PersistentCollection($actualData[$name]);
}
$coll->setOwner($document, $mapping);
$coll->setDirty( ! $coll->isEmpty());
$class->reflFields[$name]->setValue($document, $coll);
$actualData[$name] = $coll;
} elseif ($class->isSingleValuedEmbed($name) && is_object($actualData[$name])) {
$embeddedDocument = $actualData[$name];
$embeddedMetadata = $this->dm->getClassMetadata(get_class($embeddedDocument));
$actualData[$name] = array();
foreach ($embeddedMetadata->fieldMappings as $mapping) {
$actualData[$name][$mapping['fieldName']] = $embeddedMetadata->getFieldValue($embeddedDocument, $mapping['fieldName']);
}
$actualData[$name]['originalObject'] = $embeddedDocument;
}
}
$actualData = $this->getDocumentActualData($document);

if ( ! isset($this->originalDocumentData[$oid])) {
// Document is either NEW or MANAGED but not yet fully persisted (only has an id).
Expand Down
58 changes: 58 additions & 0 deletions tests/Doctrine/ODM/MongoDB/Tests/Functional/Ticket/MODM52Test.php
@@ -0,0 +1,58 @@
<?php

namespace Doctrine\ODM\MongoDB\Tests\Functional\Ticket;

require_once __DIR__ . '/../../../../../../TestInit.php';

class MODM52Test extends \Doctrine\ODM\MongoDB\Tests\BaseTest
{
public function testTest()
{
$emb = new MODM52Embedded(array(new MODM52Embedded(), new MODM52Embedded()));
$doc = new MODM52Doc(array($emb));

$this->dm->persist($doc);
$this->dm->flush(array('safe' => true));
$this->dm->refresh($doc);

// change nested embedded collection:
$doc->getItem(0)->removeItem(1);
$before = count($doc->getItem(0)->getItems());

$this->dm->persist($doc);
$this->dm->flush();
$this->dm->refresh($doc);

$after = count($doc->getItem(0)->getItems());
$this->assertEquals(1, $before);
$this->assertEquals(1, $after);
}
}

/**
* @MappedSuperClass
*/
class MODM52Container
{
/** @String */
protected $tmp = 'ensureSaved';

/** @EmbedMany(targetDocument="MODM52Embedded", cascade="all", strategy="set") */
protected $items = array();

function __construct($items = null) {if($items) $this->items = $items;}
function getItems() {return $this->items;}
function getItem($index) {return $this->items[$index];}
function removeItem($i) {unset($this->items[$i]);}
}

/** @EmbeddedDocument */
class MODM52Embedded extends MODM52Container
{}

/** @Document(db="tests", collection="tests") */
class MODM52Doc extends MODM52Container
{
/** @Id */
protected $id;
}

0 comments on commit 3e71c6a

Please sign in to comment.