diff --git a/CHANGELOG.md b/CHANGELOG.md index fe11a32..a714d30 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,13 @@ All notable changes to `Weasley` will be documented in this file. - `Controllers` directory to `Routes` directory - `Middleware` directory to `Handlers` directory - `Illuminate` directory to `Packages` directory +- `Validators` directory to `Validate` directory ### Deprecated - `Controllers` directory (use `Routes` directory instead) - `Middleware` directory (use `Handlers` directory instead) - `Illuminate` directory (use `Packages` directory instead) +- `Validators` directory (use `Validate` directory instead) ### Fixed - Unit tests in running `SessionIntegration` @@ -173,4 +175,4 @@ All notable changes to `Weasley` will be documented in this file. ## 0.1.0 - 2017-05-02 ### Added -- `Weasley` library \ No newline at end of file +- `Weasley` library diff --git a/README.md b/README.md index 9bba66d..d28e369 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,54 @@ Mutators are classes that mutates (transforms) specified result (e.g., [PSR-07]( **NOTE**: The `Laravel/Paginate` package must be included to use the parsing capabilities of `RestMutator`. +### Validation + +Weasley also provides a validation class on top of [Valitron](https://github.com/vlucas/valitron). Kindly create a class that extends to the `Check` class: + +``` php +use Rougin\Weasley\Check; + +class UserCheck extends Check +{ + protected $labels = + [ + 'name' => 'Name', + 'email' => 'Email', + 'age' => 'Age', + ]; + + protected $rules = + [ + 'name' => 'required', + 'setting' => 'required|email', + 'type' => 'required|numeric', + ]; +} +``` + +Once created, the data can be submitted to the said class for validation: + +``` php +$check = new UserCheck; + +$data = /* e.g., data from request */; + +if ($check->valid($data)) +{ + // $data passed from validation +} +else +{ + // Get the available errors --- + $errors = $check->errors(); + // ---------------------------- + + // Or get the first error only --- + echo $check->firstError(); + // ------------------------------- +} +``` + ## Changelog Please see [CHANGELOG][link-changelog] for more information what has changed recently. diff --git a/src/Check.php b/src/Check.php index 7d73d49..01232f3 100644 --- a/src/Check.php +++ b/src/Check.php @@ -2,68 +2,10 @@ namespace Rougin\Weasley; -use Valitron\Validator; - /** * @package Weasley * @author Rougin Gutib */ -abstract class Check +class Check extends Validate\Check { - /** - * @var array - */ - public $errors = array(); - - /** - * @var \Valitron\Validator - */ - protected $validator; - - public function __construct() - { - $this->validator = new Validator; - } - - /** - * Sets the labels in the validator. - * - * @return array - */ - abstract protected function labels(); - - /** - * Sets the rules in the validator. - * - * @param array $data - * @return void - */ - abstract protected function rules(array $data = array()); - - /** - * Validates the given data against the specified rules. - * - * @param array $data - * @return boolean - */ - public function validate(array $data) - { - $this->validator->labels($this->labels()); - - $this->rules($data); - - $validator = $this->validator->withData($data); - - if ($validator->validate()) - { - return true; - } - - /** @var array */ - $errors = $validator->errors(); - - $this->errors = $errors; - - return false; - } } diff --git a/src/Commands/MakeValidatorCommand.php b/src/Commands/MakeValidatorCommand.php index c4d4829..dfad508 100644 --- a/src/Commands/MakeValidatorCommand.php +++ b/src/Commands/MakeValidatorCommand.php @@ -19,6 +19,11 @@ class MakeValidatorCommand extends CreateCheck */ protected $command = 'make:validator'; + /** + * @var string + */ + protected $filename = 'Validate.stub'; + /** * @var boolean */ diff --git a/src/Scripts/Templates/Check.stub b/src/Scripts/Templates/Check.stub index 41a05d8..ae48025 100644 --- a/src/Scripts/Templates/Check.stub +++ b/src/Scripts/Templates/Check.stub @@ -13,27 +13,16 @@ use Rougin\Weasley\Check; class $CLASS extends Check { /** - * Sets the labels in the validator. - * - * @return array + * @var array */ - protected function labels() - { - $labels = array(); - + protected $labels = array( // - - return $labels; - } + ); /** - * Sets the rules in the validator. - * - * @param array $data - * @return void + * @var array */ - protected function rules(array $data = array()) - { + protected $rules = array( // - } + ); } diff --git a/src/Scripts/Templates/Validate.stub b/src/Scripts/Templates/Validate.stub new file mode 100644 index 0000000..19d66ef --- /dev/null +++ b/src/Scripts/Templates/Validate.stub @@ -0,0 +1,39 @@ + + */ + protected function labels() + { + $labels = array(); + + // + + return $labels; + } + + /** + * Sets the rules in the validator. + * + * @param array $data + * @return void + */ + protected function rules(array $data = array()) + { + // + } +} diff --git a/src/Validate/Check.php b/src/Validate/Check.php new file mode 100644 index 0000000..1edf999 --- /dev/null +++ b/src/Validate/Check.php @@ -0,0 +1,156 @@ + + */ +class Check +{ + /** + * @var array + */ + protected $errors = array(); + + /** + * @var array + */ + protected $data = array(); + + /** + * @var array + */ + protected $labels = array(); + + /** + * @var array + */ + protected $rules = array(); + + /** + * Returns the generated errors after validation. + * + * @return array + */ + public function errors() + { + return $this->errors; + } + + /** + * Returns the first error after validation. + * + * @return string|null + */ + public function firstError() + { + if (! $this->errors) + { + return null; + } + + /** @var string[][] */ + $values = array_values($this->errors); + + return (string) $values[0][0]; + } + + /** + * Returns the specified labels. + * + * @return array + */ + public function labels() + { + return $this->labels; + } + + /** + * Returns the specified rules based from the payload. + * + * @param array $data + * @return array + */ + public function rules($data) + { + return $this->rules; + } + + /** + * Adds a new error message to the specified key. + * + * @param string $key + * @param string $text + * @return self + */ + public function setError($key, $text) + { + if (! isset($this->errors[$key])) + { + $this->errors[$key] = array(); + } + + array_push($this->errors[$key], $text); + + return $this; + } + + /** + * Checks if the payload is valid againsts the specified rules. + * + * @param array|null $data + * @return boolean + */ + public function valid(array $data = null) + { + $valid = new Validator; + + $valid->labels($this->labels()); + + if (! $data) + { + $data = $this->data; + } + + $rules = $this->rules($data); + + foreach ($rules as $key => $rule) + { + // Break down multiple rules --- + $items = explode('|', $rule); + + // TODO: Allow custom rules from existing, new ones --- + foreach ($items as $item) + { + $valid->rule($item, $key); + } + // ---------------------------------------------------- + // ----------------------------- + } + + $valid = $valid->withData($data); + + if ($valid->validate()) + { + return count($this->errors) === 0; + } + + /** @var array */ + $result = $valid->errors(); + + foreach ($result as $name => $errors) + { + foreach ($errors as $error) + { + $this->setError($name, $error); + } + } + + return count($this->errors) === 0; + } +} diff --git a/src/Validators/AbstractValidator.php b/src/Validators/AbstractValidator.php index aac12d6..15535f9 100644 --- a/src/Validators/AbstractValidator.php +++ b/src/Validators/AbstractValidator.php @@ -2,16 +2,70 @@ namespace Rougin\Weasley\Validators; -use Rougin\Weasley\Check; +use Valitron\Validator; /** - * @deprecated Use "Check" instead. - * * Abstract Validator * * @package Weasley * @author Rougin Gutib */ -abstract class AbstractValidator extends Check +abstract class AbstractValidator { + /** + * @var array + */ + public $errors = array(); + + /** + * @var \Valitron\Validator + */ + protected $validator; + + public function __construct() + { + $this->validator = new Validator; + } + + /** + * Sets the labels in the validator. + * + * @return array + */ + abstract protected function labels(); + + /** + * Sets the rules in the validator. + * + * @param array $data + * @return void + */ + abstract protected function rules(array $data = array()); + + /** + * Validates the given data against the specified rules. + * + * @param array $data + * @return boolean + */ + public function validate(array $data) + { + $this->validator->labels($this->labels()); + + $this->rules($data); + + $validator = $this->validator->withData($data); + + if ($validator->validate()) + { + return true; + } + + /** @var array */ + $errors = $validator->errors(); + + $this->errors = $errors; + + return false; + } } diff --git a/tests/Commands/MakeValidatorCommandTest.php b/tests/Commands/MakeValidatorCommandTest.php index 9dfba67..8596883 100644 --- a/tests/Commands/MakeValidatorCommandTest.php +++ b/tests/Commands/MakeValidatorCommandTest.php @@ -47,4 +47,31 @@ public function testExecuteMethod() unlink($filename); } + + /** + * Tests MakeValidatorCommand::execute. + * + * @return void + */ + public function test_make_check_command() + { + $command = new CommandTester($this->console->find('make:check')); + + $command->execute(array('name' => 'TestCheck')); + + $expected = __DIR__ . '/../../src/Checks/TestCheck.php'; + $filename = $expected; + /** @var string */ + $expected = file_get_contents($expected); + $expected = str_replace("\r\n", "\n", $expected); + + $original = __DIR__ . '/../Fixture/Templates/TestCheck.php'; + /** @var string */ + $original = file_get_contents($original); + $original = str_replace("\r\n", "\n", $original); + + $this->assertEquals($expected, $original); + + unlink($filename); + } } diff --git a/tests/Fixture/Checks/UserCheck.php b/tests/Fixture/Checks/UserCheck.php new file mode 100644 index 0000000..e9d70c9 --- /dev/null +++ b/tests/Fixture/Checks/UserCheck.php @@ -0,0 +1,47 @@ + + */ +class UserCheck extends Check +{ + /** + * Sets the labels in the validator. + * + * @return array + */ + public function labels() + { + $labels = array('name' => 'Name'); + + $labels['username'] = 'Username'; + + $labels['password'] = 'Password'; + + return $labels; + } + + /** + * Sets the rules in the validator. + * + * @param array $data + * @return array + */ + public function rules($data) + { + $rules = array('name' => 'required'); + + $rules['username'] = 'required'; + + $rules['password'] = 'required'; + + return (array) $rules; + } +} diff --git a/tests/Fixture/Checks/UserCheckWithData.php b/tests/Fixture/Checks/UserCheckWithData.php new file mode 100644 index 0000000..3ab267c --- /dev/null +++ b/tests/Fixture/Checks/UserCheckWithData.php @@ -0,0 +1,40 @@ + + */ +class UserCheckWithData extends Check +{ + /** + * @var array + */ + protected $data = array( + 'name' => 'Rougin', + 'password' => '1234', + ); + + /** + * @var array + */ + protected $labels = array( + 'name' => 'Name', + 'username' => 'Username', + 'password' => 'Password', + ); + + /** + * @var array + */ + protected $rules = array( + 'name' => 'required', + 'username' => 'required', + 'password' => 'required', + ); +} diff --git a/tests/Fixture/Templates/TestCheck.php b/tests/Fixture/Templates/TestCheck.php new file mode 100644 index 0000000..2e0a857 --- /dev/null +++ b/tests/Fixture/Templates/TestCheck.php @@ -0,0 +1,28 @@ + + */ +class TestCheck extends Check +{ + /** + * @var array + */ + protected $labels = array( + // + ); + + /** + * @var array + */ + protected $rules = array( + // + ); +} diff --git a/tests/Fixture/Templates/TestValidator.php b/tests/Fixture/Templates/TestValidator.php index cd53033..d4d586f 100644 --- a/tests/Fixture/Templates/TestValidator.php +++ b/tests/Fixture/Templates/TestValidator.php @@ -2,7 +2,7 @@ namespace App\Validators; -use Rougin\Weasley\Check; +use Rougin\Weasley\Validators\AbstractValidator; /** * TestValidator @@ -10,7 +10,7 @@ * @package App * @author Rougin Gutib */ -class TestValidator extends Check +class TestValidator extends AbstractValidator { /** * Sets the labels in the validator. diff --git a/tests/Validate/CheckTest.php b/tests/Validate/CheckTest.php new file mode 100644 index 0000000..63888e8 --- /dev/null +++ b/tests/Validate/CheckTest.php @@ -0,0 +1,107 @@ + + */ +class CheckTest extends Testcase +{ + /** + * @return void + */ + public function test_passed_validation() + { + $check = new UserCheck; + + $data = array('name' => 'Rougin'); + + $data['username'] = 'rougin'; + + $data['password'] = '12345'; + + $result = $check->valid($data); + + $this->assertTrue($result); + } + + /** + * @return void + */ + public function test_failed_validation() + { + $check = new UserCheck; + + $data = array('name' => 'Rougin'); + + $data['username'] = 'rougin'; + + $check->valid($data); + + $expected = array('password' => array()); + $expected['password'][] = 'Password is required'; + + $actual = $check->errors(); + + $this->assertEquals($expected, $actual); + } + + /** + * @return void + */ + public function test_failed_validation_with_data() + { + $check = new UserCheckWithData; + + $check->valid(); + + $expected = array('username' => array()); + $expected['username'][] = 'Username is required'; + + $actual = $check->errors(); + + $this->assertEquals($expected, $actual); + } + + /** + * @return void + */ + public function test_first_error_from_failed_validation() + { + $check = new UserCheckWithData; + + $check->valid(); + + $expected = 'Username is required'; + + $actual = $check->firstError(); + + $this->assertEquals($expected, $actual); + } + + /** + * @return void + */ + public function test_first_error_from_passed_validation() + { + $check = new UserCheck; + + $data = array('name' => 'Rougin'); + + $data['username'] = 'rougin'; + + $data['password'] = '12345'; + + $check->valid($data); + + $actual = $check->firstError(); + + $this->assertNull($actual); + } +}