ソースを参照

Handle composite primary keys when merging many.

mergeMany() needs to be composite key aware as tables/associations can
easily have composite keys. I've used `;` as a separator as I've
not seen many composite keys with them, and even when they do an out of
place `;` will still enforce uniqueness.

Refs #4817
Mark Story 11 年 前
コミット
207c86a200
2 ファイル変更43 行追加2 行削除
  1. 13 2
      src/ORM/Marshaller.php
  2. 30 0
      tests/TestCase/ORM/MarshallerTest.php

+ 13 - 2
src/ORM/Marshaller.php

@@ -347,7 +347,18 @@ class Marshaller {
  */
 	public function mergeMany($entities, array $data, array $options = []) {
 		$primary = (array)$this->_table->primaryKey();
-		$indexed = (new Collection($data))->groupBy($primary[0])->toArray();
+		$isSimple = !is_array($primary);
+
+		$data = new Collection($data);
+		$data = $data->groupBy(function ($el) use ($primary) {
+			$keys = [];
+			foreach ($primary as $key) {
+				$keys[] = isset($el[$key]) ? $el[$key] : '';
+			}
+			return implode(';', $keys);
+		});
+		$indexed = $data->toArray();
+
 		$new = isset($indexed[null]) ? [$indexed[null]] : [];
 		unset($indexed[null]);
 		$output = [];
@@ -357,7 +368,7 @@ class Marshaller {
 				continue;
 			}
 
-			$key = $entity->get($primary[0]);
+			$key = implode(';', $entity->extract($primary));
 
 			if ($key === null || !isset($indexed[$key])) {
 				continue;

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

@@ -1038,6 +1038,36 @@ class MarshallerTest extends TestCase {
 	}
 
 /**
+ * Test that mergeMany() handles composite key associations properly.
+ *
+ * The articles_tags table has a composite primary key, and should be
+ * handled correctly.
+ *
+ * @return void
+ */
+	public function testMergeManyCompositeKey() {
+		$articlesTags = TableRegistry::get('ArticlesTags');
+
+		$entities = [
+			new OpenEntity(['article_id' => 1, 'tag_id' => 2]),
+			new OpenEntity(['article_id' => 1, 'tag_id' => 1]),
+		];
+		$entities[0]->clean();
+		$entities[1]->clean();
+
+		$data = [
+			['article_id' => 1, 'tag_id' => 1],
+			['article_id' => 1, 'tag_id' => 2]
+		];
+		$marshall = new Marshaller($articlesTags);
+		$result = $marshall->mergeMany($entities, $data);
+
+		$this->assertCount(2, $result, 'Should have two records');
+		$this->assertSame($entities[0], $result[0], 'Should retain object');
+		$this->assertSame($entities[1], $result[1], 'Should retain object');
+	}
+
+/**
  * Tests merge with data types that need to be marshalled
  *
  * @return void