Skip to content

Commit

Permalink
Allow invalid field data to be kept along with the error messages in …
Browse files Browse the repository at this point in the history
…the Entity.
  • Loading branch information
Mark Scherer committed Jan 3, 2016
1 parent dd659e2 commit b1b8dac
Show file tree
Hide file tree
Showing 5 changed files with 85 additions and 1 deletion.
49 changes: 48 additions & 1 deletion src/Datasource/EntityTrait.php
Expand Up @@ -95,6 +95,13 @@ trait EntityTrait
*/
protected $_errors = [];

/**
* List of invalid fields and their data for errors upon validation/patching
*
* @var array
*/
protected $_invalid = [];

/**
* Map of properties in this entity that can be safely assigned, each
* property name points to a boolean indicating its status. An empty array
Expand Down Expand Up @@ -600,7 +607,7 @@ public function dirty($property = null, $isDirty = null)
}

$this->_dirty[$property] = true;
unset($this->_errors[$property]);
unset($this->_errors[$property], $this->_invalid[$property]);
return true;
}

Expand All @@ -615,6 +622,7 @@ public function clean()
{
$this->_dirty = [];
$this->_errors = [];
$this->_invalid = [];
}

/**
Expand Down Expand Up @@ -779,6 +787,44 @@ protected function _readError($object, $path = null)
return [];
}

/**
* Sets a field as invalid and not patchable into the entity.
*
* This is useful for batch operations when one needs to get the original value for an error message after patching.
* This value could not be patched into the entity and is simply copied into the _invalid property for debugging purposes
* or to be able to log it away.
*
* @param string|array|null $field
* @param string|null $value
* @param bool $overwrite
* @return $this|mixed
*/
public function invalid($field = null, $value = null, $overwrite = false)
{
if ($field === null) {
return $this->_invalid;
}

if (is_string($field) && $value === null) {
$value = isset($this->_invalid[$field]) ? $this->_invalid[$field] : null;
return $value;
}

if (!is_array($field)) {
$field = [$field => $value];
}

foreach ($field as $f => $value) {
if ($overwrite) {
$this->_invalid[$f] = $value;
continue;
}
$this->_invalid += [$f => $value];
}

return $this;
}

/**
* Stores whether or not a property value can be changed or set in this entity.
* The special property `*` can also be marked as accessible or protected, meaning
Expand Down Expand Up @@ -881,6 +927,7 @@ public function __debugInfo()
'[original]' => $this->_original,
'[virtual]' => $this->_virtual,
'[errors]' => $this->_errors,
'[invalid]' => $this->_invalid,
'[repository]' => $this->_registryAlias
];
}
Expand Down
8 changes: 8 additions & 0 deletions src/Datasource/RulesChecker.php
Expand Up @@ -327,6 +327,14 @@ protected function _addError($rule, $name, $options)
$message = [$message];
}
$entity->errors($options['errorField'], $message);

if (method_exists($entity, 'invalid')) {
$invalid = null;
if (isset($entity->{$options['errorField']})) {
$invalid = $entity->{$options['errorField']};
}
$entity->invalid($options['errorField'], $invalid);
}
return $pass === true;
};
}
Expand Down
6 changes: 6 additions & 0 deletions src/ORM/Marshaller.php
Expand Up @@ -128,6 +128,9 @@ public function one(array $data, array $options = [])
$properties = [];
foreach ($data as $key => $value) {
if (!empty($errors[$key])) {
if (method_exists($entity, 'invalid')) {
$entity->invalid($key, $value);
}
continue;
}
$columnType = $schema->columnType($key);
Expand Down Expand Up @@ -463,6 +466,9 @@ public function merge(EntityInterface $entity, array $data, array $options = [])
$properties = $marshalledAssocs = [];
foreach ($data as $key => $value) {
if (!empty($errors[$key])) {
if (method_exists($entity, 'invalid')) {
$entity->invalid($key, $value);
}
continue;
}

Expand Down
22 changes: 22 additions & 0 deletions tests/TestCase/ORM/MarshallerTest.php
Expand Up @@ -2523,6 +2523,24 @@ public function testPassingCustomValidator()
$this->assertNotEmpty($entity->errors('thing'));
}

/**
* Tests that invalid propery is being filled when data cannot be patched into an entity
*
* @return void
*/
public function testValidationWithInvalidFilled() {
$data = [
'title' => 'foo',
'number' => 'bar',
];
$validator = (new Validator)->add('number', 'numeric', ['rule' => 'numeric']);
$marshall = new Marshaller($this->articles);
$entity = $marshall->one($data, ['validate' => $validator]);
$this->assertNotEmpty($entity->errors('number'));
$this->assertNull($entity->number);
$this->assertSame(['number' => 'bar'], $entity->invalid());
}

/**
* Test merge with validation error
*
Expand All @@ -2541,6 +2559,8 @@ public function testMergeWithValidation()
'body' => 'My Content',
'author_id' => 1
]);
$this->assertEmpty($entity->invalid());

$entity->accessible('*', true);
$entity->isNew(false);
$entity->clean();
Expand All @@ -2561,7 +2581,9 @@ public function testMergeWithValidation()

$this->articles->validator()->requirePresence('thing', 'create');
$result = $marshall->merge($entity, $data, []);

$this->assertEmpty($result->errors('thing'));
$this->assertSame(['author_id' => 'foo'], $result->invalid());
}

/**
Expand Down
1 change: 1 addition & 0 deletions tests/TestCase/ORM/RulesCheckerIntegrationTest.php
Expand Up @@ -116,6 +116,7 @@ function (Entity $entity) {
$this->assertNull($entity->article->get('author_id'));
$this->assertFalse($entity->article->dirty('author_id'));
$this->assertNotEmpty($entity->article->errors('title'));
$this->assertSame('A Title', $entity->article->invalid('title'));
}

/**
Expand Down

0 comments on commit b1b8dac

Please sign in to comment.