Skip to content

Commit

Permalink
Correctly marshall _joinData when patching entities.
Browse files Browse the repository at this point in the history
When patching belongsToMany associations with existing target records,
and including _joinData, the joinData should be correctly marshalled.

Getting this to work required accepting `accessibleFields` in merge()
and mergeMany(). This allows the internals of the marshaller to
whitelist _joinData and not rely on the entity having it marked as
accessible.

Refs #6971
  • Loading branch information
markstory committed Jul 8, 2015
1 parent 97c3dcc commit 1cb6645
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/ORM/Marshaller.php
Expand Up @@ -408,6 +408,7 @@ protected function _loadBelongsToMany($assoc, $ids)
* also be set to a string to use a specific validator. Defaults to true/default.
* * fieldList: A whitelist of fields to be assigned to the entity. If not present
* the accessible fields list in the entity will be used.
* * accessibleFields: A list of fields to allow or deny in entity accessible fields.
*
* @param \Cake\Datasource\EntityInterface $entity the entity that will get the
* data merged in
Expand All @@ -427,6 +428,12 @@ public function merge(EntityInterface $entity, array $data, array $options = [])
$keys = $entity->extract((array)$this->_table->primaryKey());
}

if (isset($options['accessibleFields'])) {
foreach ((array)$options['accessibleFields'] as $key => $value) {
$entity->accessible($key, $value);
}
}

$errors = $this->_validate($data + $keys, $options, $isNew);
$schema = $this->_table->schema();
$properties = $marshalledAssocs = [];
Expand Down Expand Up @@ -501,6 +508,7 @@ public function merge(EntityInterface $entity, array $data, array $options = [])
* - associated: Associations listed here will be marshalled as well.
* - fieldList: A whitelist of fields to be assigned to the entity. If not present,
* the accessible fields list in the entity will be used.
* - accessibleFields: A list of fields to allow or deny in entity accessible fields.
*
* @param array|\Traversable $entities the entities that will get the
* data merged in
Expand Down Expand Up @@ -660,6 +668,7 @@ protected function _mergeJoinData($original, $assoc, $value, $options)
$nested = (array)$associated['_joinData'];
}

$options['accessibleFields'] = ['_joinData' => true];
$records = $this->mergeMany($original, $value, $options);
foreach ($records as $record) {
$hash = spl_object_hash($record);
Expand Down
91 changes: 91 additions & 0 deletions tests/TestCase/ORM/MarshallerTest.php
Expand Up @@ -34,6 +34,17 @@ class OpenEntity extends Entity
];
}

/**
* Test entity for mass assignment.
*/
class Tag extends Entity
{

protected $_accessible = [
'tag' => true,
];
}

/**
* Test entity for mass assignment.
*/
Expand Down Expand Up @@ -125,6 +136,7 @@ public function setUp()
$this->articles = $articles;
$this->comments = $comments;
$this->users = $users;
$this->tags = $tags;
}

/**
Expand Down Expand Up @@ -625,6 +637,57 @@ public function testBelongsToManyWithMixedJoinData()
$this->assertEquals(1, $result->tags[1]->_joinData->active);
}

/**
* Test belongsToMany association with mixed data and _joinData
*
* @return void
*/
public function testBelongsToManyAddingNewExisting()
{
$this->tags->entityClass(__NAMESPACE__ . '\Tag');
$data = [
'title' => 'My title',
'body' => 'My content',
'author_id' => 1,
'tags' => [
[
'id' => 1,
'_joinData' => [
'active' => 0,
]
],
]
];
$marshall = new Marshaller($this->articles);
$result = $marshall->one($data, ['associated' => ['Tags._joinData']]);
$data = [
'title' => 'New Title',
'tags' => [
[
'id' => 1,
'_joinData' => [
'active' => 0,
]
],
[
'id' => 2,
'_joinData' => [
'active' => 1,
]
]
]
];
$result = $marshall->merge($result, $data, ['associated' => ['Tags._joinData']]);

$this->assertEquals($data['title'], $result->title);
$this->assertEquals($data['tags'][0]['id'], $result->tags[0]->id);
$this->assertEquals($data['tags'][1]['id'], $result->tags[1]->id);
$this->assertNotEmpty($result->tags[0]->_joinData);
$this->assertNotEmpty($result->tags[1]->_joinData);
$this->assertEquals(0, $result->tags[0]->_joinData->active);
$this->assertEquals(1, $result->tags[1]->_joinData->active);
}

/**
* Test belongsToMany association with mixed data and _joinData
*
Expand Down Expand Up @@ -970,6 +1033,34 @@ public function testMergeSimple()
$this->assertFalse($result->isNew(), 'Should not change the entity state');
}

/**
* Test merge() with accessibleFields options
*
* @return void
*/
public function testMergeAccessibleFields()
{
$data = [
'title' => 'My title',
'body' => 'New content',
'author_id' => 1,
'not_in_schema' => true
];
$marshall = new Marshaller($this->articles);
$entity = new Entity([
'title' => 'Foo',
'body' => 'My Content'
]);
$entity->accessible('*', false);
$entity->isNew(false);
$entity->clean();
$result = $marshall->merge($entity, $data, ['accessibleFields' => ['body' => true]]);

$this->assertSame($entity, $result);
$this->assertEquals(['title' => 'Foo', 'body' => 'New content'], $result->toArray());
$this->assertTrue($entity->accessible('body'));
}

/**
* Provides empty values.
*
Expand Down

0 comments on commit 1cb6645

Please sign in to comment.