Browse Source

Make one()/many() more consistent with empty associations.

When an association is cleaned out by a beforeMarshal hook, the
entity property should not be marked as dirty as the related entity
has no properties to persist.

Refs #10658
Mark Story 8 years ago
parent
commit
873cf9ce5b
2 changed files with 50 additions and 0 deletions
  1. 8 0
      src/ORM/Marshaller.php
  2. 42 0
      tests/TestCase/ORM/MarshallerTest.php

+ 8 - 0
src/ORM/Marshaller.php

@@ -212,6 +212,14 @@ class Marshaller
             $entity->set($properties);
         }
 
+        // Don't flag clean association entities as
+        // dirty so we don't persist empty records.
+        foreach ($properties as $field => $value) {
+            if ($value instanceof EntityInterface) {
+                $entity->dirty($field, $value->dirty());
+            }
+        }
+
         $entity->setErrors($errors);
 
         return $entity;

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

@@ -367,6 +367,48 @@ class MarshallerTest extends TestCase
     }
 
     /**
+     * Test that one() correctly handles an association beforeMarshal
+     * making the association empty.
+     *
+     * @return void
+     */
+    public function testOneAssociationBeforeMarshalMutation()
+    {
+        $users = TableRegistry::get('Users');
+        $articles = TableRegistry::get('Articles');
+
+        $users->hasOne('Articles', [
+            'foreignKey' => 'author_id'
+        ]);
+        $articles->eventManager()->on('Model.beforeMarshal', function ($event, $data, $options) {
+            // Blank the association, so it doesn't become dirty.
+            unset($data['not_a_real_field']);
+        });
+
+        $data = [
+            'username' => 'Jen',
+            'article' => [
+                'not_a_real_field' => 'whatever'
+            ]
+        ];
+        $marshall = new Marshaller($users);
+        $entity = $marshall->one($data, ['associated' => ['Articles']]);
+        $this->assertTrue($entity->isDirty('username'));
+        $this->assertFalse($entity->isDirty('article'));
+
+        // Ensure consistency with merge()
+        $entity = new Entity([
+            'username' => 'Jenny',
+        ]);
+        // Make the entity think it is new.
+        $entity->accessible('*', true);
+        $entity->clean();
+        $entity = $marshall->merge($entity, $data, ['associated' => ['Articles']]);
+        $this->assertTrue($entity->isDirty('username'));
+        $this->assertFalse($entity->isDirty('article'));
+    }
+
+    /**
      * Test one() supports accessibleFields option for associations
      *
      * @return void