Browse Source

Merge pull request #3939 from cakephp/3.0-fieldlist

3.0 - unify options for newEntity() and save()
José Lorenzo Rodríguez 11 years ago
parent
commit
247e13168f

+ 8 - 10
src/Datasource/RepositoryInterface.php

@@ -146,11 +146,10 @@ interface RepositoryInterface {
  * is saved. Until the entity is saved, it will be a detached record.
  *
  * @param array $data The data to build an entity with.
- * @param array $associations A whitelist of associations
- *   to hydrate. Defaults to all associations
+ * @param array $options A list of options for the object hydration.
  * @return \Cake\Datasource\EntityInterface
  */
-	public function newEntity(array $data = [], $associations = null);
+	public function newEntity(array $data = [], array $options = []);
 
 /**
  * Create a list of entities + associated entities from an array.
@@ -165,11 +164,10 @@ interface RepositoryInterface {
  * The hydrated entities can then be iterated and saved.
  *
  * @param array $data The data to build an entity with.
- * @param array $associations A whitelist of associations
- *   to hydrate. Defaults to all associations
+ * @param array $options A list of options for the objects hydration.
  * @return array An array of hydrated records.
  */
-	public function newEntities(array $data, $associations = null);
+	public function newEntities(array $data, array $options = []);
 
 /**
  * Merges the passed `$data` into `$entity` respecting the accessible
@@ -185,10 +183,10 @@ interface RepositoryInterface {
  * @param \Cake\Datasource\EntityInterface $entity the entity that will get the
  * data merged in
  * @param array $data key value list of fields to be merged into the entity
- * @param array $associations The list of associations to be merged
+ * @param array $options A list of options for the object hydration.
  * @return \Cake\Datasource\EntityInterface
  */
-	public function patchEntity(EntityInterface $entity, array $data, $associations = null);
+	public function patchEntity(EntityInterface $entity, array $data, array $options = []);
 
 /**
  * Merges each of the elements passed in `$data` into the entities
@@ -205,9 +203,9 @@ interface RepositoryInterface {
  * @param array|\Traversable $entities the entities that will get the
  * data merged in
  * @param array $data list of arrays to be merged into the entities
- * @param array $associations The list of associations to be merged
+ * @param array $options A list of options for the objects hydration.
  * @return array
  */
-	public function patchEntities($entities, array $data, $associations = null);
+	public function patchEntities($entities, array $data, array $options = []);
 
 }

+ 54 - 52
src/ORM/Marshaller.php

@@ -37,13 +37,6 @@ class Marshaller {
 	use AssociationsNormalizerTrait;
 
 /**
- * Whether or not this marhshaller is in safe mode.
- *
- * @var bool
- */
-	protected $_safe;
-
-/**
  * The table instance this marshaller is for.
  *
  * @var \Cake\ORM\Table
@@ -54,20 +47,23 @@ class Marshaller {
  * Constructor.
  *
  * @param \Cake\ORM\Table $table The table this marshaller is for.
- * @param bool $safe Whether or not this marshaller is in safe mode
  */
-	public function __construct(Table $table, $safe = false) {
+	public function __construct(Table $table) {
 		$this->_table = $table;
-		$this->_safe = $safe;
 	}
 
 /**
  * Build the map of property => association names.
  *
- * @param array $include The array of included associations.
+ * @param array $options List of options containing the 'associated' key.
  * @return array
  */
-	protected function _buildPropertyMap($include) {
+	protected function _buildPropertyMap($options) {
+		if (empty($options['associated'])) {
+			return [];
+		}
+
+		$include = $options['associated'];
 		$map = [];
 		$include = $this->_normalizeAssociations($include);
 		foreach ($include as $key => $nested) {
@@ -91,12 +87,13 @@ class Marshaller {
  * Hydrate one entity and its associated data.
  *
  * @param array $data The data to hydrate.
- * @param array $include The associations to include.
+ * @param array $options List of options, if the 'associated' key is present
+ * associations listed there will be marshalled as well.
  * @return \Cake\ORM\Entity
  * @see \Cake\ORM\Table::newEntity()
  */
-	public function one(array $data, array $include = []) {
-		$propertyMap = $this->_buildPropertyMap($include);
+	public function one(array $data, array $options = []) {
+		$propertyMap = $this->_buildPropertyMap($options);
 
 		$schema = $this->_table->schema();
 		$tableName = $this->_table->alias();
@@ -113,7 +110,7 @@ class Marshaller {
 			$columnType = $schema->columnType($key);
 			if (isset($propertyMap[$key])) {
 				$assoc = $propertyMap[$key]['association'];
-				$nested = $propertyMap[$key]['nested'];
+				$nested = ['associated' => $propertyMap[$key]['nested']];
 				$value = $this->_marshalAssociation($assoc, $value, $nested);
 			} elseif ($columnType) {
 				$converter = Type::build($columnType);
@@ -130,34 +127,35 @@ class Marshaller {
  *
  * @param \Cake\ORM\Association $assoc The association to marshall
  * @param array $value The data to hydrate
- * @param array $include The associations to include.
+ * @param array $options List of options.
  * @return mixed
  */
-	protected function _marshalAssociation($assoc, $value, $include) {
+	protected function _marshalAssociation($assoc, $value, $options) {
 		$targetTable = $assoc->target();
 		$marshaller = $targetTable->marshaller();
 		$types = [Association::ONE_TO_ONE, Association::MANY_TO_ONE];
 		if (in_array($assoc->type(), $types)) {
-			return $marshaller->one($value, (array)$include);
+			return $marshaller->one($value, (array)$options);
 		}
 		if ($assoc->type() === Association::MANY_TO_MANY) {
-			return $marshaller->_belongsToMany($assoc, $value, (array)$include);
+			return $marshaller->_belongsToMany($assoc, $value, (array)$options);
 		}
-		return $marshaller->many($value, (array)$include);
+		return $marshaller->many($value, (array)$options);
 	}
 
 /**
  * Hydrate many entities and their associated data.
  *
  * @param array $data The data to hydrate.
- * @param array $include The associations to include.
+ * @param array $options List of options, if the 'associated' key is present
+ * associations listed there will be marshelled as well.
  * @return array An array of hydrated records.
  * @see \Cake\ORM\Table::newEntities()
  */
-	public function many(array $data, array $include = []) {
+	public function many(array $data, array $options = []) {
 		$output = [];
 		foreach ($data as $record) {
-			$output[] = $this->one($record, $include);
+			$output[] = $this->one($record, $options);
 		}
 		return $output;
 	}
@@ -170,10 +168,11 @@ class Marshaller {
  *
  * @param Association $assoc The association to marshal.
  * @param array $data The data to convert into entities.
- * @param array $include The nested associations to convert.
+ * @param array $options List of options.
  * @return array An array of built entities.
  */
-	protected function _belongsToMany(Association $assoc, array $data, $include = []) {
+	protected function _belongsToMany(Association $assoc, array $data, $options = []) {
+		$associated = isset($options['associated']) ? $options['associated'] : [];
 		$hasIds = array_key_exists('_ids', $data);
 		if ($hasIds && is_array($data['_ids'])) {
 			return $this->_loadBelongsToMany($assoc, $data['_ids']);
@@ -182,13 +181,13 @@ class Marshaller {
 			return [];
 		}
 
-		$records = $this->many($data, $include);
+		$records = $this->many($data, $options);
 		$joint = $assoc->junction();
 		$jointMarshaller = $joint->marshaller();
 
 		$nested = [];
-		if (isset($include['_joinData']['associated'])) {
-			$nested = (array)$include['_joinData']['associated'];
+		if (isset($associated['_joinData']['associated'])) {
+			$nested = ['associated' => (array)$associated['_joinData']['associated']];
 		}
 
 		foreach ($records as $i => $record) {
@@ -226,7 +225,7 @@ class Marshaller {
 
 /**
  * Merges `$data` into `$entity` and recursively does the same for each one of
- * the association names passed in `$include`. When merging associations, if an
+ * the association names passed in `$options`. When merging associations, if an
  * entity is not present in the parent entity for a given association, a new one
  * will be created.
  *
@@ -237,11 +236,12 @@ class Marshaller {
  * @param \Cake\Datasource\EntityInterface $entity the entity that will get the
  * data merged in
  * @param array $data key value list of fields to be merged into the entity
- * @param array $include The list of associations to be merged
+ * @param array $options List of options, if the 'associated' key is present
+ * associations listed there will be marshalled as well.
  * @return \Cake\Datasource\EntityInterface
  */
-	public function merge(EntityInterface $entity, array $data, array $include = []) {
-		$propertyMap = $this->_buildPropertyMap($include);
+	public function merge(EntityInterface $entity, array $data, array $options = []) {
+		$propertyMap = $this->_buildPropertyMap($options);
 		$tableName = $this->_table->alias();
 
 		if (isset($data[$tableName])) {
@@ -256,7 +256,7 @@ class Marshaller {
 
 			if (isset($propertyMap[$key])) {
 				$assoc = $propertyMap[$key]['association'];
-				$nested = $propertyMap[$key]['nested'];
+				$nested = ['associated' => $propertyMap[$key]['nested']];
 				$value = $this->_mergeAssociation($original, $assoc, $value, $nested);
 			} elseif ($columnType) {
 				$converter = Type::build($columnType);
@@ -276,7 +276,7 @@ class Marshaller {
 /**
  * Merges each of the elements from `$data` into each of the entities in `$entities
  * and recursively does the same for each one of the association names passed in
- * `$include`. When merging associations, if an entity is not present in the parent
+ * `$options`. When merging associations, if an entity is not present in the parent
  * entity for such association, a new one will be created.
  *
  * Records in `$data` are matched against the entities by using the primary key
@@ -291,10 +291,11 @@ class Marshaller {
  * @param array|\Traversable $entities the entities that will get the
  * data merged in
  * @param array $data list of arrays to be merged into the entities
- * @param array $include The list of associations to be merged
+ * @param array $options List of options, if the 'associated' key is present
+ * associations listed there will be marshalled as well.
  * @return array
  */
-	public function mergeMany($entities, array $data, array $include = []) {
+	public function mergeMany($entities, array $data, array $options = []) {
 		$primary = (array)$this->_table->primaryKey();
 		$indexed = (new Collection($data))->groupBy($primary[0])->toArray();
 		$new = isset($indexed[null]) ? [$indexed[null]] : [];
@@ -312,13 +313,13 @@ class Marshaller {
 				continue;
 			}
 
-			$output[] = $this->merge($entity, $indexed[$key][0], $include);
+			$output[] = $this->merge($entity, $indexed[$key][0], $options);
 			unset($indexed[$key]);
 		}
 
 		foreach (array_merge($indexed, $new) as $record) {
 			foreach ($record as $value) {
-				$output[] = $this->one($value, $include);
+				$output[] = $this->one($value, $options);
 			}
 		}
 		return $output;
@@ -330,24 +331,24 @@ class Marshaller {
  * @param \Cake\Datasource\EntityInterface $original The original entity
  * @param \Cake\ORM\Association $assoc The association to merge
  * @param array $value The data to hydrate
- * @param array $include The associations to include.
+ * @param array $options List of options.
  * @return mixed
  */
-	protected function _mergeAssociation($original, $assoc, $value, $include) {
+	protected function _mergeAssociation($original, $assoc, $value, $options) {
 		if (!$original) {
-			return $this->_marshalAssociation($assoc, $value, $include);
+			return $this->_marshalAssociation($assoc, $value, $options);
 		}
 
 		$targetTable = $assoc->target();
 		$marshaller = $targetTable->marshaller();
 		$types = [Association::ONE_TO_ONE, Association::MANY_TO_ONE];
 		if (in_array($assoc->type(), $types)) {
-			return $marshaller->merge($original, $value, (array)$include);
+			return $marshaller->merge($original, $value, (array)$options);
 		}
 		if ($assoc->type() === Association::MANY_TO_MANY) {
-			return $marshaller->_mergeBelongsToMany($original, $assoc, $value, (array)$include);
+			return $marshaller->_mergeBelongsToMany($original, $assoc, $value, (array)$options);
 		}
-		return $marshaller->mergeMany($original, $value, (array)$include);
+		return $marshaller->mergeMany($original, $value, (array)$options);
 	}
 
 /**
@@ -357,11 +358,12 @@ class Marshaller {
  * @param \Cake\Datasource\EntityInterface $original The original entity
  * @param \Cake\ORM\Association $assoc The association to marshall
  * @param array $value The data to hydrate
- * @param array $include The associations to include.
+ * @param array $options List of options.
  * @return mixed
  */
-	protected function _mergeBelongsToMany($original, $assoc, $value, $include) {
+	protected function _mergeBelongsToMany($original, $assoc, $value, $options) {
 		$hasIds = array_key_exists('_ids', $value);
+		$associated = isset($options['associated']) ? $options['associated'] : [];
 		if ($hasIds && is_array($value['_ids'])) {
 			return $this->_loadBelongsToMany($assoc, $value['_ids']);
 		}
@@ -369,8 +371,8 @@ class Marshaller {
 			return [];
 		}
 
-		if (!in_array('_joinData', $include) && !isset($include['_joinData'])) {
-			return $this->mergeMany($original, $value, $include);
+		if (!in_array('_joinData', $associated) && !isset($associated['_joinData'])) {
+			return $this->mergeMany($original, $value, $options);
 		}
 
 		$extra = [];
@@ -385,11 +387,11 @@ class Marshaller {
 		$marshaller = $joint->marshaller();
 
 		$nested = [];
-		if (isset($include['_joinData']['associated'])) {
-			$nested = (array)$include['_joinData']['associated'];
+		if (isset($associated['_joinData']['associated'])) {
+			$nested = ['associated' => (array)$associated['_joinData']['associated']];
 		}
 
-		$records = $this->mergeMany($original, $value, $include);
+		$records = $this->mergeMany($original, $value, $options);
 		foreach ($records as $record) {
 			$hash = spl_object_hash($record);
 			$value = $record->get('_joinData');

+ 21 - 23
src/ORM/Table.php

@@ -1576,13 +1576,11 @@ class Table implements RepositoryInterface, EventListener {
  * Override this method if you want a table object to use custom
  * marshalling logic.
  *
- * @param bool $safe Whether or not this marshaller
- *   should be in safe mode.
  * @return \Cake\ORM\Marshaller
  * @see \Cake\ORM\Marshaller
  */
-	public function marshaller($safe = false) {
-		return new Marshaller($this, $safe);
+	public function marshaller() {
+		return new Marshaller($this);
 	}
 
 /**
@@ -1602,22 +1600,22 @@ class Table implements RepositoryInterface, EventListener {
  *
  * By default all the associations on this table will be hydrated. You can
  * limit which associations are built, or include deeper associations
- * using the associations parameter:
+ * using the options parameter:
  *
  * {{{
  * $articles = $this->Articles->newEntity(
  *   $this->request->data(),
- *   ['Tags', 'Comments.Users']
+ *   ['associated' => ['Tags', 'Comments.Users']]
  * );
  * }}}
  *
  */
-	public function newEntity(array $data = [], $associations = null) {
-		if ($associations === null) {
-			$associations = $this->_associations->keys();
+	public function newEntity(array $data = [], array $options = []) {
+		if (!isset($options['associated'])) {
+			$options['associated'] = $this->_associations->keys();
 		}
 		$marshaller = $this->marshaller();
-		return $marshaller->one($data, $associations);
+		return $marshaller->one($data, $options);
 	}
 
 /**
@@ -1625,19 +1623,19 @@ class Table implements RepositoryInterface, EventListener {
  *
  * By default all the associations on this table will be hydrated. You can
  * limit which associations are built, or include deeper associations
- * using the associations parameter:
+ * using the options parameter:
  *
  * {{{
  * $articles = $this->Articles->newEntities(
  *   $this->request->data(),
- *   ['Tags', 'Comments.Users']
+ *   ['associated' => ['Tags', 'Comments.Users']]
  * );
  * }}}
  *
  */
-	public function newEntities(array $data, $associations = null) {
-		if ($associations === null) {
-			$associations = $this->_associations->keys();
+	public function newEntities(array $data, array $options = []) {
+		if (!isset($options['associated'])) {
+			$options['associated'] = $this->_associations->keys();
 		}
 		$marshaller = $this->marshaller();
 		return $marshaller->many($data, $associations);
@@ -1650,12 +1648,12 @@ class Table implements RepositoryInterface, EventListener {
  * `$data` array will appear, those that can be matched by primary key will get
  * the data merged, but those that cannot, will be discarded.
  */
-	public function patchEntity(EntityInterface $entity, array $data, $associations = null) {
-		if ($associations === null) {
-			$associations = $this->_associations->keys();
+	public function patchEntity(EntityInterface $entity, array $data, array $options = []) {
+		if (!isset($options['associated'])) {
+			$options['associated'] = $this->_associations->keys();
 		}
 		$marshaller = $this->marshaller();
-		return $marshaller->merge($entity, $data, $associations);
+		return $marshaller->merge($entity, $data, $options);
 	}
 
 /**
@@ -1669,12 +1667,12 @@ class Table implements RepositoryInterface, EventListener {
  * `$data` array will appear, those that can be matched by primary key will get
  * the data merged, but those that cannot, will be discarded.
  */
-	public function patchEntities($entities, array $data, $associations = null) {
-		if ($associations === null) {
-			$associations = $this->_associations->keys();
+	public function patchEntities($entities, array $data, array $options = []) {
+		if (!isset($options['associated'])) {
+			$options['associated'] = $this->_associations->keys();
 		}
 		$marshaller = $this->marshaller();
-		return $marshaller->mergeMany($entities, $data, $associations);
+		return $marshaller->mergeMany($entities, $data, $options);
 	}
 
 /**

+ 2 - 2
tests/TestCase/ORM/CompositeKeysTest.php

@@ -444,7 +444,7 @@ class CompositeKeyTest extends TestCase {
 			'tags' => ['_ids' => [[1, 1], [2, 2], [3, 1]]]
 		];
 		$marshall = new Marshaller($articles);
-		$result = $marshall->one($data, ['SiteTags']);
+		$result = $marshall->one($data, ['associated' => ['SiteTags']]);
 
 		$this->assertCount(3, $result->tags);
 		$this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
@@ -457,7 +457,7 @@ class CompositeKeyTest extends TestCase {
 			'tags' => ['_ids' => [1, 2, 3]]
 		];
 		$marshall = new Marshaller($articles);
-		$result = $marshall->one($data, ['SiteTags']);
+		$result = $marshall->one($data, ['associated' => ['SiteTags']]);
 		$this->assertEmpty($result->tags);
 	}
 

+ 20 - 20
tests/TestCase/ORM/MarshallerTest.php

@@ -202,7 +202,7 @@ class MarshallerTest extends TestCase {
 			]
 		];
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->one($data, ['Users']);
+		$result = $marshall->one($data, ['associated' => ['Users']]);
 
 		$this->assertInstanceOf('Cake\ORM\Entity', $result);
 		$this->assertTrue($result->dirty(), 'Should be a dirty entity.');
@@ -231,7 +231,7 @@ class MarshallerTest extends TestCase {
 			]
 		];
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->one($data, ['Users']);
+		$result = $marshall->one($data, ['associated' => ['Users']]);
 
 		$this->assertEquals($data['title'], $result->title);
 		$this->assertEquals($data['body'], $result->body);
@@ -265,7 +265,7 @@ class MarshallerTest extends TestCase {
 			]
 		];
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->one($data, ['Comments']);
+		$result = $marshall->one($data, ['associated' => ['Comments']]);
 
 		$this->assertEquals($data['title'], $result->title);
 		$this->assertEquals($data['body'], $result->body);
@@ -298,7 +298,7 @@ class MarshallerTest extends TestCase {
 		];
 		$marshall = new Marshaller($this->articles);
 		$result = $marshall->one($data, [
-			'Tags'
+			'associated' => ['Tags']
 		]);
 
 		$this->assertEquals($data['title'], $result->title);
@@ -352,7 +352,7 @@ class MarshallerTest extends TestCase {
 		$articlesTags->belongsTo('Users');
 
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->one($data, ['Tags._joinData.Users']);
+		$result = $marshall->one($data, ['associated' => ['Tags._joinData.Users']]);
 		$this->assertInstanceOf(
 			'Cake\ORM\Entity',
 			$result->tags[0]->_joinData->user,
@@ -386,7 +386,7 @@ class MarshallerTest extends TestCase {
 			]
 		];
 		$marshall = new Marshaller($this->comments);
-		$result = $marshall->one($data, ['Articles.Users']);
+		$result = $marshall->one($data, ['associated' => ['Articles.Users']]);
 
 		$this->assertEquals(
 			$data['article']['title'],
@@ -441,7 +441,7 @@ class MarshallerTest extends TestCase {
 			],
 		];
 		$marshall = new Marshaller($this->comments);
-		$result = $marshall->many($data, ['Users']);
+		$result = $marshall->many($data, ['associated' => ['Users']]);
 
 		$this->assertCount(2, $result);
 		$this->assertInstanceOf('Cake\ORM\Entity', $result[0]);
@@ -468,7 +468,7 @@ class MarshallerTest extends TestCase {
 			'tags' => ['_ids' => '']
 		];
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->one($data, ['Tags']);
+		$result = $marshall->one($data, ['associated' => ['Tags']]);
 		$this->assertCount(0, $result->tags);
 
 		$data = [
@@ -476,7 +476,7 @@ class MarshallerTest extends TestCase {
 			'body' => 'Some content here',
 			'tags' => ['_ids' => false]
 		];
-		$result = $marshall->one($data, ['Tags']);
+		$result = $marshall->one($data, ['associated' => ['Tags']]);
 		$this->assertCount(0, $result->tags);
 
 		$data = [
@@ -484,7 +484,7 @@ class MarshallerTest extends TestCase {
 			'body' => 'Some content here',
 			'tags' => ['_ids' => null]
 		];
-		$result = $marshall->one($data, ['Tags']);
+		$result = $marshall->one($data, ['associated' => ['Tags']]);
 		$this->assertCount(0, $result->tags);
 
 		$data = [
@@ -493,7 +493,7 @@ class MarshallerTest extends TestCase {
 			'tags' => ['_ids' => [1, 2, 3]]
 		];
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->one($data, ['Tags']);
+		$result = $marshall->one($data, ['associated' => ['Tags']]);
 
 		$this->assertCount(3, $result->tags);
 		$this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
@@ -612,7 +612,7 @@ class MarshallerTest extends TestCase {
 			]
 		];
 		$marshall = new Marshaller($this->articles);
-		$marshall->merge($entity, $data, ['Users']);
+		$marshall->merge($entity, $data, ['associated' => ['Users']]);
 		$this->assertEquals('My Content', $entity->body);
 		$this->assertSame($user, $entity->user);
 		$this->assertEquals('mark', $entity->user->username);
@@ -639,7 +639,7 @@ class MarshallerTest extends TestCase {
 			]
 		];
 		$marshall = new Marshaller($this->articles);
-		$marshall->merge($entity, $data, ['Users']);
+		$marshall->merge($entity, $data, ['associated' => ['Users']]);
 		$this->assertEquals('My Content', $entity->body);
 		$this->assertInstanceOf('Cake\ORM\Entity', $entity->user);
 		$this->assertEquals('mark', $entity->user->username);
@@ -680,7 +680,7 @@ class MarshallerTest extends TestCase {
 			]
 		];
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->merge($entity, $data, ['Users', 'Comments']);
+		$result = $marshall->merge($entity, $data, ['associated' => ['Users', 'Comments']]);
 		$this->assertSame($entity, $result);
 		$this->assertSame($user, $result->user);
 		$this->assertEquals('not so secret', $entity->user->password);
@@ -724,7 +724,7 @@ class MarshallerTest extends TestCase {
 		];
 		$entity->accessible('*', true);
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->merge($entity, $data, ['Tags']);
+		$result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
 
 		$this->assertCount(3, $result->tags);
 		$this->assertInstanceOf('Cake\ORM\Entity', $result->tags[0]);
@@ -754,21 +754,21 @@ class MarshallerTest extends TestCase {
 		];
 		$entity->accessible('*', true);
 		$marshall = new Marshaller($this->articles);
-		$result = $marshall->merge($entity, $data, ['Tags']);
+		$result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
 		$this->assertCount(0, $result->tags);
 
 		$data = [
 			'title' => 'Haz moar tags',
 			'tags' => ['_ids' => false]
 		];
-		$result = $marshall->merge($entity, $data, ['Tags']);
+		$result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
 		$this->assertCount(0, $result->tags);
 
 		$data = [
 			'title' => 'Haz moar tags',
 			'tags' => ['_ids' => null]
 		];
-		$result = $marshall->merge($entity, $data, ['Tags']);
+		$result = $marshall->merge($entity, $data, ['associated' => ['Tags']]);
 		$this->assertCount(0, $result->tags);
 	}
 
@@ -800,7 +800,7 @@ class MarshallerTest extends TestCase {
 			],
 		];
 
-		$options = ['Tags' => ['associated' => ['_joinData']]];
+		$options = ['associated' => ['Tags' => ['associated' => ['_joinData']]]];
 		$marshall = new Marshaller($this->articles);
 		$entity = $marshall->one($data, $options);
 		$entity->accessible('*', true);
@@ -864,7 +864,7 @@ class MarshallerTest extends TestCase {
 		$articlesTags = TableRegistry::get('ArticlesTags');
 		$articlesTags->belongsTo('Users');
 
-		$options = ['Tags._joinData.Users'];
+		$options = ['associated' => ['Tags._joinData.Users']];
 		$marshall = new Marshaller($this->articles);
 		$entity = $marshall->one($data, $options);
 		$entity->accessible('*', true);

+ 4 - 5
tests/TestCase/ORM/QueryRegressionTest.php

@@ -225,14 +225,13 @@ class QueryRegressionTest extends TestCase {
 				]
 			]
 		];
-		$entity = $articles->patchEntity($entity, $data, [
-			'Highlights._joinData.Authors', 'Highlights.Authors'
-		]);
-		$articles->save($entity, [
+		$options = [
 			'associated' => [
 				'Highlights._joinData.Authors', 'Highlights.Authors'
 			]
-		]);
+		];
+		$entity = $articles->patchEntity($entity, $data, $options);
+		$articles->save($entity, $options);
 		$entity = $articles->get(2, [
 			'contain' => [
 				'SpecialTags' => ['sort' => ['SpecialTags.id' => 'ASC']],

+ 2 - 2
tests/TestCase/ORM/TableTest.php

@@ -3375,7 +3375,7 @@ class TableTest extends \Cake\TestSuite\TestCase {
 		$data = ['foo' => 'bar'];
 		$marshaller->expects($this->once())
 			->method('merge')
-			->with($entity, $data, ['users', 'articles'])
+			->with($entity, $data, ['associated' => ['users', 'articles']])
 			->will($this->returnValue($entity));
 		$table->patchEntity($entity, $data);
 	}
@@ -3398,7 +3398,7 @@ class TableTest extends \Cake\TestSuite\TestCase {
 		$data = [['foo' => 'bar']];
 		$marshaller->expects($this->once())
 			->method('mergeMany')
-			->with($entities, $data, ['users', 'articles'])
+			->with($entities, $data, ['associated' => ['users', 'articles']])
 			->will($this->returnValue($entities));
 		$table->patchEntities($entities, $data);
 	}