Browse Source

Fix joinData being lost when belongsToMany is marshalled by id.

When patching belongsToMany records by id, the existing joinData record
should be retained and not replaced.

Refs #8159
Mark Story 10 years ago
parent
commit
4ad72ad83a
2 changed files with 39 additions and 1 deletions
  1. 9 1
      src/ORM/Marshaller.php
  2. 30 0
      tests/TestCase/ORM/MarshallerTest.php

+ 9 - 1
src/ORM/Marshaller.php

@@ -704,19 +704,27 @@ class Marshaller
         }
 
         $options['accessibleFields'] = ['_joinData' => true];
+
         $records = $this->mergeMany($original, $value, $options);
         foreach ($records as $record) {
             $hash = spl_object_hash($record);
             $value = $record->get('_joinData');
 
+            // Already an entity, no further marshalling required.
+            if ($value instanceof EntityInterface) {
+                continue;
+            }
+
+            // Scalar data can't be handled
             if (!is_array($value)) {
                 $record->unsetProperty('_joinData');
                 continue;
             }
 
+            // Marshal data into the old object, or make a new joinData object.
             if (isset($extra[$hash])) {
                 $record->set('_joinData', $marshaller->merge($extra[$hash], $value, $nested));
-            } else {
+            } elseif (is_array($value)) {
                 $joinData = $marshaller->one($value, $nested);
                 $record->set('_joinData', $joinData);
             }

+ 30 - 0
tests/TestCase/ORM/MarshallerTest.php

@@ -2004,6 +2004,36 @@ class MarshallerTest extends TestCase
     }
 
     /**
+     * Tests that merging belongsToMany association doesn't erase _joinData
+     * on existing objects.
+     *
+     * @return void
+     */
+    public function testMergeBelongsToManyIdsRetainJoinData()
+    {
+        $this->articles->belongsToMany('Tags');
+        $entity = $this->articles->get(1, ['contain' => ['Tags']]);
+        $entity->accessible('*', true);
+        $original = $entity->tags[0]->_joinData;
+
+        $this->assertInstanceOf('Cake\ORM\Entity', $entity->tags[0]->_joinData);
+
+        $data = [
+            'title' => 'Haz moar tags',
+            'tags' => [
+                ['id' => 1],
+            ]
+        ];
+        $marshall = new Marshaller($this->articles);
+        $result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
+
+        $this->assertCount(1, $result->tags);
+        $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
+        $this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]->_joinData);
+        $this->assertSame($original, $result->tags[0]->_joinData, 'Should be same object');
+    }
+
+    /**
      * Test mergeMany() with a simple set of data.
      *
      * @return void