Skip to content
Permalink
Browse files

Implement nested field validators.

This adds support for nested field validators. To better support
model-less forms and the elasticsearch ODM, we will need to validate
nested fields. This set of changes is the smallest change I could make
that implements the basic requirements.

Refs #6496
  • Loading branch information...
markstory committed May 7, 2015
1 parent 56085cd commit a86b0c7dbaca7d3abd8764185690f5dc823a2d78
Showing with 61 additions and 6 deletions.
  1. +9 −6 src/Validation/Validator.php
  2. +52 −0 tests/TestCase/Validation/ValidatorTest.php
@@ -17,6 +17,7 @@
use ArrayAccess;
use Cake\Validation\RulesProvider;
use Cake\Validation\ValidationSet;
use Cake\Utility\Hash;
use Countable;
use IteratorAggregate;
@@ -97,25 +98,27 @@ public function errors(array $data, $newRecord = true)
$requiredMessage = __d('cake', 'This field is required');
$emptyMessage = __d('cake', 'This field cannot be left empty');
}
$flat = Hash::flatten($data);
foreach ($this->_fields as $name => $field) {
$keyPresent = array_key_exists($name, $data);
$isPath = strpos($name, '.') !== false;
$keyPresent = array_key_exists($name, $isPath ? $flat : $data);
if (!$keyPresent && !$this->_checkPresence($field, $newRecord)) {
$errors[$name]['_required'] = isset($this->_presenceMessages[$name])
? $this->_presenceMessages[$name]
: $requiredMessage;
continue;
}
if (!$keyPresent) {
continue;
}
$value = $isPath ? $flat[$name] : $data[$name];
$providers = $this->_providers;
$context = compact('data', 'newRecord', 'field', 'providers');
$canBeEmpty = $this->_canBeEmpty($field, $context);
$isEmpty = $this->_fieldIsEmpty($data[$name]);
$isEmpty = $this->_fieldIsEmpty($value);
if (!$canBeEmpty && $isEmpty) {
$errors[$name]['_empty'] = isset($this->_allowEmptyMessages[$name])
@@ -128,7 +131,7 @@ public function errors(array $data, $newRecord = true)
continue;
}
$result = $this->_processRules($name, $field, $data, $newRecord);
$result = $this->_processRules($name, $value, $field, $data, $newRecord);
if ($result) {
$errors[$name] = $result;
}
@@ -547,13 +550,13 @@ protected function _fieldIsEmpty($data)
*
* @param string $field The name of the field that is being processed
* @param ValidationSet $rules the list of rules for a field
* @param mixed $value The field value.
* @param array $data the full data passed to the validator
* @param bool $newRecord whether is it a new record or an existing one
* @return array
*/
protected function _processRules($field, ValidationSet $rules, $data, $newRecord)
protected function _processRules($field, $value, ValidationSet $rules, $data, $newRecord)
{
$value = $data[$field];
$errors = [];
// Loading default provider in case there is none
$this->provider('default');
@@ -46,6 +46,31 @@ public function testAddingRulesToField()
$this->assertCount(2, $validator);
}
/**
* Testing you can add nested field rules
*
* @return void
*/
public function testAddingNestedRulesToField()
{
$validator = new Validator;
$validator->add('user.username', 'not-blank', ['rule' => 'notBlank']);
$this->assertCount(0, $validator->field('user'));
$set = $validator->field('user.username');
$this->assertInstanceOf('Cake\Validation\ValidationSet', $set);
$this->assertCount(1, $set);
$validator->add('user.username', 'letters', ['rule' => 'alphanumeric']);
$this->assertCount(2, $set);
$validator->remove('user.username', 'letters');
$this->assertCount(1, $set);
$validator->requirePresence('user.twitter');
$this->assertTrue($validator->field('user.twitter')->isPresenceRequired());
}
/**
* Tests that calling field will create a default validation set for it
*
@@ -163,6 +188,33 @@ public function testErrorsWithPresenceRequired()
$this->assertEmpty($validator->errors(['foo' => 'bar']));
}
/**
* Test that errors() can work with nested data.
*
* @return void
*/
public function testErrorsWithNestedFields()
{
$validator = new Validator;
$validator->add('user.username', 'letter', ['rule' => 'alphanumeric']);
$validator->add('comments.0.comment', 'letter', ['rule' => 'alphanumeric']);
$data = [
'user' => [
'username' => 'is wrong'
],
'comments' => [
['comment' => 'is wrong']
]
];
$errors = $validator->errors($data);
$expected = [
'user.username' => ['letter' => 'The provided value is invalid'],
'comments.0.comment' => ['letter' => 'The provided value is invalid']
];
$this->assertEquals($expected, $errors);
}
/**
* Tests custom error messages generated when a field presence is required
*

0 comments on commit a86b0c7

Please sign in to comment.
You can’t perform that action at this time.