Skip to content

Commit

Permalink
Implement path based error reading.
Browse files Browse the repository at this point in the history
Much like other parts of CakePHP, errors in entities should be readable
using a dot separated path.

* Implement code.
* Fix tests.

Refs #3807
  • Loading branch information
markstory committed Jul 10, 2014
1 parent 3d22390 commit 42cdf10
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 23 deletions.
67 changes: 49 additions & 18 deletions src/Datasource/EntityTrait.php
Expand Up @@ -16,6 +16,7 @@

use Cake\Utility\Inflector;
use Cake\Validation\Validator;
use \Traversable;

/**
* An entity represents a single result row from a repository. It exposes the
Expand Down Expand Up @@ -578,10 +579,10 @@ public function errors($field = null, $errors = null) {

if (is_string($field) && $errors === null) {
$errors = isset($this->_errors[$field]) ? $this->_errors[$field] : [];
if (!$errors) {
$errors = $this->_nestedErrors($field);
if ($errors) {
return $errors;
}
return $errors;
return $this->_nestedErrors($field);
}

if (!is_array($field)) {
Expand All @@ -591,7 +592,6 @@ public function errors($field = null, $errors = null) {
foreach ($field as $f => $error) {
$this->_errors[$f] = (array)$error;
}

return $this;
}

Expand All @@ -602,26 +602,57 @@ public function errors($field = null, $errors = null) {
* @return array errors in nested entity if any
*/
protected function _nestedErrors($field) {
if (!isset($this->_properties[$field])) {
return [];
$path = explode('.', $field);

// Only one path element, check for nested entity with error.
if (count($path) === 1) {
return $this->_readError($this->get($path[0]));
}

$value = $this->_properties[$field];
$errors = [];
if (is_array($value) || $value instanceof \Traversable) {
foreach ($value as $k => $v) {
if (!($v instanceof self)) {
break;
}
$errors[$k] = $v->errors();
$entity = $this;
while (count($path)) {
$part = array_shift($path);
if ($entity instanceof static) {
$val = $entity->get($part);
} elseif (is_array($entity)) {
$val = isset($entity[$part]) ? $entity[$part] : false;
}
return $errors;
}

if ($value instanceof self) {
return $value->errors();
if (
is_array($val) ||
$val instanceof Traversable ||
$val instanceof static
) {
$entity = $val;
} else {
$path[] = $part;
break;
}
}
if (count($path) <= 1) {
return $this->_readError($entity, array_pop($path));
}
return [];
}

/**
* Read the error(s) from one or many objects.
*
* @param array|\Cake\Datasource\EntityTrait $object The object to read errors from.
* @param string $path The field name for errors.
* @return array
*/
protected function _readError($object, $path = null) {
if ($object instanceof static) {
return $object->errors($path);
}
if (is_array($object)) {
return array_map(function($val) {
if ($val instanceof static) {
return $val->errors();
}
}, $object);
}
return [];
}

Expand Down
11 changes: 6 additions & 5 deletions tests/TestCase/ORM/EntityTest.php
Expand Up @@ -839,11 +839,12 @@ public function testErrorPathReading() {
$entity->errors('wrong', 'Bad stuff');
$assoc->errors('nope', 'Terrible things');

$this->assertEquals('Bad stuff', $entity->errors('wrong'));
$this->assertEquals('Terrible things', $entity->errors('many.0.nope'));
$this->assertEquals('Terrible things', $entity->errors('one.nope'));
$this->assertEquals(['nope' => 'Terrible things'], $entity->errors('one'));
$this->assertEquals(['nope' => 'Terrible things'], $entity->errors('many.0'));
$this->assertEquals(['Bad stuff'], $entity->errors('wrong'));
$this->assertEquals(['Terrible things'], $entity->errors('many.0.nope'));
$this->assertEquals(['Terrible things'], $entity->errors('one.nope'));
$this->assertEquals(['nope' => ['Terrible things']], $entity->errors('one'));
$this->assertEquals([0 => ['nope' => ['Terrible things']]], $entity->errors('many'));
$this->assertEquals(['nope' => ['Terrible things']], $entity->errors('many.0'));

$this->assertEquals([], $entity->errors('many.0.mistake'));
$this->assertEquals([], $entity->errors('one.mistake'));
Expand Down

0 comments on commit 42cdf10

Please sign in to comment.