ソースを参照

Merge pull request #7036 from cakephp/optimize-belongs-to-many

Avoiding bonus UPDATE statements when saving belongsToMany associations
José Lorenzo Rodríguez 10 年 前
コミット
948d2c4b70
2 ファイル変更48 行追加5 行削除
  1. 11 5
      src/ORM/Association/BelongsToMany.php
  2. 37 0
      tests/TestCase/ORM/TableTest.php

+ 11 - 5
src/ORM/Association/BelongsToMany.php

@@ -543,11 +543,17 @@ class BelongsToMany extends Association
                 $joint = new $entityClass([], ['markNew' => true, 'source' => $junctionAlias]);
             }
 
-            $joint->set(array_combine(
-                $foreignKey,
-                $sourceEntity->extract($bindingKey)
-            ), ['guard' => false]);
-            $joint->set(array_combine($assocForeignKey, $e->extract($targetPrimaryKey)), ['guard' => false]);
+            $sourceKeys = array_combine($foreignKey, $sourceEntity->extract($bindingKey));
+            $targetKeys = array_combine($assocForeignKey, $e->extract($targetPrimaryKey));
+
+            if ($sourceKeys !== $joint->extract($foreignKey)) {
+                $joint->set($sourceKeys, ['guard' => false]);
+            }
+
+            if ($targetKeys !== $joint->extract($assocForeignKey)) {
+                $joint->set($targetKeys, ['guard' => false]);
+            }
+
             $saved = $junction->save($joint, $options);
 
             if (!$saved && !empty($options['atomic'])) {

+ 37 - 0
tests/TestCase/ORM/TableTest.php

@@ -4383,6 +4383,43 @@ class TableTest extends TestCase
     }
 
     /**
+     * Tests that on second save, entities for the belongsToMany relation are not marked
+     * as dirty unnecessarily. This helps avoid wasteful database statements and makes
+     * for a cleaner transaction log
+     *
+     * @return void
+     */
+    public function testSaveBelongsToManyNoWasteSave()
+    {
+        $data = [
+            'title' => 'foo',
+            'body' => 'bar',
+            'tags' => [
+                '_ids' => [1, 2]
+            ]
+        ];
+
+        $table = TableRegistry::get('Articles');
+        $table->belongsToMany('Tags');
+        $article = $table->save($table->newEntity($data, ['associated' => ['Tags']]));
+
+        $counter = 0;
+        $table->Tags->junction()
+            ->eventManager()
+            ->on('Model.afterSave', function ($event, $entity) use (&$counter) {
+                if ($entity->dirty()) {
+                    $counter++;
+                }
+            });
+
+        $article->tags[] = $table->Tags->get(3);
+        $this->assertCount(3, $article->tags);
+        $article->dirty('tags', true);
+        $table->save($article);
+        $this->assertEquals(1, $counter);
+    }
+
+    /**
      * Tests that after saving then entity contains the right primary
      * key casted to the right type
      *