From a6f96be4993246c321129f34287d609bb3dc6680 Mon Sep 17 00:00:00 2001 From: Ken Lalobo Date: Tue, 28 Jun 2016 16:13:53 +0100 Subject: [PATCH] Add the ability to define a pretty name for an item --- README.md | 9 +++++++-- src/TypeValidator/AbstractTypeValidator.php | 5 +++-- src/TypeValidator/ArrayValidator.php | 12 +++++++----- src/TypeValidator/NumberValidator.php | 14 ++++++++------ src/TypeValidator/ObjectValidator.php | 19 +++++++++++-------- src/TypeValidator/StringValidator.php | 15 ++++++++------- src/TypeValidator/TypeValidatorInterface.php | 3 ++- src/Validator.php | 8 +++++--- .../phpunit/src/Functional/ValidatorTest.php | 18 +++++++++++------- 9 files changed, 62 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index 1223455..f749f3b 100644 --- a/README.md +++ b/README.md @@ -35,6 +35,7 @@ The libray allows you to validate a json style data structure using a set of val $rules = [ 'name' => [ + 'name' => 'Name', 'required' => true, 'type' => 'string', 'constraints' => [ @@ -42,6 +43,7 @@ The libray allows you to validate a json style data structure using a set of val ] ], 'age' => [ + 'name' => 'Age', 'required' => false, 'type' => 'number', 'constraints' => [ @@ -49,6 +51,7 @@ The libray allows you to validate a json style data structure using a set of val ] ], 'address' => [ + 'name' => 'Address', 'required' => false, 'type' => 'object', 'properties' => [ @@ -112,8 +115,9 @@ The rules follow a simple structure. There are two categories of rules. Named, a ``` $rules = [ 'name' => [ - 'required' => true, - 'type' => 'string', + 'name' => 'Name', + 'required' => true, + 'type' => 'string', 'constraints' => [ 'callback' => 'MyValidator::checkName' ] @@ -137,6 +141,7 @@ $rules = [ All rules have a mandatory `type` and an optional `constraints` property. Additionaly, all named rules have a mandatory `required` property. * **properties** + * **name** [*string*]: The human readable name of your item (this is optional) * **required** [*true/false*]: Wether the item is required or not (For named rules only) * **type** [*string*]: The type of item. Currently `string`, `number`, `array` or `object` * **constraints** [*array*] : an optional associative array of constraints. These are: diff --git a/src/TypeValidator/AbstractTypeValidator.php b/src/TypeValidator/AbstractTypeValidator.php index e5b4ac1..0fccf6e 100644 --- a/src/TypeValidator/AbstractTypeValidator.php +++ b/src/TypeValidator/AbstractTypeValidator.php @@ -23,13 +23,14 @@ class AbstractTypeValidator implements TypeValidatorInterface * * @param array $constraints The rules * @param mixed $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @throws DataValidationException */ - public function validate(array $constraints, $data) + public function validate(array $constraints, $data, $prettyName = 'This value') { if (isset($constraints['callback'])) { - call_user_func_array($constraints['callback'], array($data)); + call_user_func_array($constraints['callback'], array($data, $prettyName)); } } } diff --git a/src/TypeValidator/ArrayValidator.php b/src/TypeValidator/ArrayValidator.php index 394b981..a16f2d4 100644 --- a/src/TypeValidator/ArrayValidator.php +++ b/src/TypeValidator/ArrayValidator.php @@ -23,15 +23,16 @@ class ArrayValidator extends AbstractTypeValidator * * @param array $constraints The rules * @param mixed $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @throws DataValidationException */ - public function validate(array $constraints, $data) + public function validate(array $constraints, $data, $prettyName = 'This value') { if (gettype($data) == 'array') { - $this->validateSequentialArray($data); + $this->validateSequentialArray($data, $prettyName); } else { - throw new DataValidationException('This value must be a sequential array'); + throw new DataValidationException(sprintf('%s must be a sequential array', $prettyName)); } } @@ -39,14 +40,15 @@ public function validate(array $constraints, $data) * Validate that array is sequential * * @param array $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @throws DataValidationException */ - public function validateSequentialArray(array $data) + public function validateSequentialArray(array $data, $prettyName = 'This value') { //if the array has to have keys that are both numeric AND sequential if (!empty($data) && array_keys($data) !== range(0, count($data) - 1)) { - throw new DataValidationException('This value is an array but it is not sequential'); + throw new DataValidationException(sprintf('%s is an array but it is not sequential', $prettyName)); } } } diff --git a/src/TypeValidator/NumberValidator.php b/src/TypeValidator/NumberValidator.php index 5dec6ac..2ee54f5 100644 --- a/src/TypeValidator/NumberValidator.php +++ b/src/TypeValidator/NumberValidator.php @@ -23,33 +23,35 @@ class NumberValidator extends AbstractTypeValidator * * @param array $constraints The rules * @param mixed $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @return boolean Wether it was valid or not */ - public function validate(array $constraints, $data) + public function validate(array $constraints, $data, $prettyName = 'This value') { if (is_numeric($data) == false) { - throw new DataValidationException('This value must be a number'); + throw new DataValidationException(sprintf('%s must be a number', $prettyName)); } if (isset($constraints['integer'])) { - $this->validateInteger($data, $constraints['integer']); + $this->validateInteger($data, $constraints['integer'], $prettyName); } - parent::validate($constraints, $data); + parent::validate($constraints, $data, $prettyName); } /** * Validate that the data is/is not an integer * * @param array $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @throws DataValidationException */ - public function validateInteger($data, $isInt) + public function validateInteger($data, $isInt, $prettyName = 'This value') { if ($isInt != is_int($data)) { - throw new DataValidationException('This value must '.($isInt == false?'not':'').' be an integer'); + throw new DataValidationException(sprintf('%s must '.($isInt == false?'not':'').' be an integer', $prettyName)); } } } diff --git a/src/TypeValidator/ObjectValidator.php b/src/TypeValidator/ObjectValidator.php index 3babc91..d352f5d 100644 --- a/src/TypeValidator/ObjectValidator.php +++ b/src/TypeValidator/ObjectValidator.php @@ -23,33 +23,35 @@ class ObjectValidator extends AbstractTypeValidator * * @param array $constraints The rules * @param mixed $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @return boolean Wether it was valid or not */ - public function validate(array $constraints, $data) + public function validate(array $constraints, $data, $prettyName = 'This value') { if (gettype($data) == 'object') { $this->validateObject($data); } elseif (gettype($data) == 'array') { - $this->validateAssociativeArray($data); + $this->validateAssociativeArray($data, $prettyName); } else { - throw new DataValidationException('This value must be a standard object or an associative array'); + throw new DataValidationException(sprintf('%s must be a standard object or an associative array', $prettyName)); } - parent::validate($constraints, $data); + parent::validate($constraints, $data, $prettyName); } /** * Validate that the data is an instance of the stdClass * * @param object $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @throws DataValidationException */ - public function validateObject($data) + public function validateObject($data, $prettyName = 'This value') { if (!$data instanceof \stdClass) { - throw new DataValidationException('This value must be an instance of the stdClass'); + throw new DataValidationException(sprintf('%s must be an instance of the stdClass', $prettyName)); } } @@ -57,14 +59,15 @@ public function validateObject($data) * Validate that the data is an associative array * * @param array $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @throws DataValidationException */ - public function validateAssociativeArray(array $data) + public function validateAssociativeArray(array $data, $prettyName = 'This value') { //if the array has keys that are both numeric AND sequential then it is not associative if (!empty($data) && array_keys($data) === range(0, count($data) - 1)) { - throw new DataValidationException('This value is an array but it is not associative'); + throw new DataValidationException(sprintf('%s is an array but it is not associative', $prettyName)); } } } diff --git a/src/TypeValidator/StringValidator.php b/src/TypeValidator/StringValidator.php index 9438c27..008212b 100644 --- a/src/TypeValidator/StringValidator.php +++ b/src/TypeValidator/StringValidator.php @@ -24,35 +24,36 @@ class StringValidator extends AbstractTypeValidator * * @param array $constraints The rules * @param mixed $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @return boolean Wether it was valid or not */ - public function validate(array $constraints, $data) + public function validate(array $constraints, $data, $prettyName = 'This value') { if (gettype($data) != 'string') { - throw new DataValidationException('This value must be a string'); + throw new DataValidationException(sprintf('%s must be a string', $prettyName)); } if (isset($constraints['length'])) { if (sizeof($constraints['length']) != 2) { throw new InvalidRuleException('The length property needs to have two members'); } - $this->validateLength($data, $constraints['length'][0], $constraints['length'][1]); + $this->validateLength($data, $constraints['length'][0], $constraints['length'][1], $prettyName); } - parent::validate($constraints, $data); + parent::validate($constraints, $data, $prettyName); } - public function validateLength($data, $min = null, $max = null) + public function validateLength($data, $min = null, $max = null, $prettyName = 'This value') { $length = strlen($data); if (isset($min) && $length < $min) { - throw new DataValidationException('This value must have a length of at least '.$min); + throw new DataValidationException(sprintf('%s must have a length of at least '.$min, $prettyName)); } if (isset($max) && $length > $max) { - throw new DataValidationException('This value must have a length less than or equal to '.$max); + throw new DataValidationException(sprintf('%s must have a length less than or equal to '.$max, $prettyName)); } return true; diff --git a/src/TypeValidator/TypeValidatorInterface.php b/src/TypeValidator/TypeValidatorInterface.php index e99a282..ff4bd49 100644 --- a/src/TypeValidator/TypeValidatorInterface.php +++ b/src/TypeValidator/TypeValidatorInterface.php @@ -18,8 +18,9 @@ interface TypeValidatorInterface * * @param array $constraints The rules * @param mixed $data The data to validate + * @param mixed $prettyName Human readable name for the data being validated * * @return boolean Whether it was valid or not */ - public function validate(array $constraints, $data); + public function validate(array $constraints, $data, $prettyName); } diff --git a/src/Validator.php b/src/Validator.php index 7b8cfcd..c10898f 100644 --- a/src/Validator.php +++ b/src/Validator.php @@ -134,7 +134,8 @@ public function validateData(array $validationRule, $itemKey, $data, $fullyQuali } if ($validationRule['required'] == true && $this->propertyExists($itemKey, $data) == false) { - throw new DataValidationException('This value is required'); + $prettyName = isset($validationRule['name'])?$validationRule['name']:'This value'; + throw new DataValidationException(sprintf('%s is required', $prettyName)); } elseif ($validationRule['required'] == false && $this->propertyExists($itemKey, $data) == false) { //The item does not exist and is not required so no need to validate return; @@ -157,8 +158,9 @@ public function validateItem(array $validationRule, $item, $fullyQualifiedName) { $typeValidator = $this->getTypeValidator($validationRule['type']); - $constraints = isset($validationRule['constraints']) ? $validationRule['constraints'] : []; - $typeValidator->validate($constraints, $item); + $constraints = isset($validationRule['constraints']) ? $validationRule['constraints'] : []; + $prettyName = isset($validationRule['name'])?$validationRule['name']:'This value'; + $typeValidator->validate($constraints, $item, $prettyName); $validationType = $validationRule['type']; diff --git a/tests/phpunit/src/Functional/ValidatorTest.php b/tests/phpunit/src/Functional/ValidatorTest.php index e944a18..3d7d82e 100644 --- a/tests/phpunit/src/Functional/ValidatorTest.php +++ b/tests/phpunit/src/Functional/ValidatorTest.php @@ -22,6 +22,7 @@ public function validate($data, $valid, $errors) { $validationRules = [ 'name' => [ + 'name' => 'Name', 'required' => true, 'type' => 'string', 'constraints' => [ @@ -30,6 +31,7 @@ public function validate($data, $valid, $errors) ] ], 'age' => [ + 'name' => 'Age', 'required' => false, 'type' => 'number', 'constraints' => [ @@ -37,6 +39,7 @@ public function validate($data, $valid, $errors) ] ], 'address' => [ + 'name' => 'Address', 'required' => false, 'type' => 'object', 'properties' => [ @@ -61,6 +64,7 @@ public function validate($data, $valid, $errors) ] ], 'nickNames' => [ + 'name' => 'Nick Names', 'required' => false, 'type' => 'array', 'items' => [ @@ -82,19 +86,19 @@ public function validate($data, $valid, $errors) public function dataToValidate() { return [ - [[], false, ['name' => ['This value is required']]], - [['name' => null], false, ['name' => ['This value must be a string']]], - [['name' => 1], false, ['name' => ['This value must be a string']]], - [['name' => ''], false, ['name' => ['This value must have a length of at least 1']]], + [[], false, ['name' => ['Name is required']]], + [['name' => null], false, ['name' => ['Name must be a string']]], + [['name' => 1], false, ['name' => ['Name must be a string']]], + [['name' => ''], false, ['name' => ['Name must have a length of at least 1']]], [['name' => 'Ken'], false, ['name' => ['This value must have two words']]], [['name' => 'Ken Lalobo'], true, []], - [['name' => 'Ken Lalobo', 'age' => 'test'], false, ['age' => ['This value must be a number']]], - [['name' => 'Ken Lalobo', 'age' => 0.1], false, ['age' => ['This value must be an integer']]], + [['name' => 'Ken Lalobo', 'age' => 'test'], false, ['age' => ['Age must be a number']]], + [['name' => 'Ken Lalobo', 'age' => 0.1], false, ['age' => ['Age must be an integer']]], [['name' => 'Ken Lalobo', 'age' => 102], true, []], [ ['name' => 'Ken Lalobo', 'age' => 102, 'address' => null], false, - ['address' => ['This value must be a standard object or an associative array']] + ['address' => ['Address must be a standard object or an associative array']] ], [ ['name' => 'Ken Lalobo', 'age' => 102, 'address' => []],