📜 ⬆️ ⬇️

Validation of complex structures with PHPixie Validate

image
Today another PHPixie 3 component came out , this time for data validation. PHP libraries that deal with validation are already enough, then why write another one? In fact, most of them have a big drawback - they only work with one-dimensional data arrays, focusing primarily on working with forms. This approach is inevitably outdated in the world of API and REST, it is increasingly necessary to work with document-based queries with a complex structure. Validate was designed from the very beginning just to cope with such tasks. And even if you are not using PHPixie, this component can be very useful to you.

Let's start with a simple example, a simple one-dimensional array:

//    $data = array( 'name' => 'Pixie', 'home' => 'Oak', 'age' => 200, 'type' => 'fairy' ); $validate = new \PHPixie\Validate(); //   $validator = $validate->validator(); //        $document = $validator->rule()->addDocument(); //       //  .    //     $document->valueField('name') ->required() ->addFilter() ->alpha() ->minLength(3); //      $document->valueField('home') ->required() ->addFilter() ->filters(array( 'alpha', 'minLength' => array(3) )); //      //     $document->valueField('age') ->required() ->filter('numeric'); //      $document->valueField('type') ->required() ->callback(function($result, $value) { if(!in_array($value, array('fairy', 'pixie'))) { //    $result->addMessageError("Type can be either 'fairy' or 'pixie'"); } }); //       //     . //      $document->allowExtraFields(); //      $validator->rule()->callback(function($result, $value) { if($value['type'] === 'fairy' && $value['home'] !== 'Oak') { $result->addMessageError("Fairies live only inside oaks"); } }); 


Same but with alternate syntax
 $validator = $validate->validator(function($value) { $value->document(function($document) { $document ->allowExtraFields() ->field('name', function($name) { $name ->required() ->filter(function($filter) { $filter ->alpha() ->minLength(3); }); }) ->field('home', function($home) { $home ->required() ->filter(array( 'alpha', 'minLength' => array(3) )); }) ->field('age', function($age) { $age ->required() ->filter('numeric'); }) ->field('type', function($home) { $home ->required() ->callback(function($result, $value) { if(!in_array($value, array('fairy', 'pixie'))) { $result->addMessageError("Type can be either 'fairy' or 'pixie'"); } }); }); }) ->callback(function($result, $value) { if($value['type'] === 'fairy' && $value['home'] !== 'Oak') { $result->addMessageError("Fairies live only inside oaks"); } }); }); 


')
And the validation itself:

 $result = $validator->validate($data); var_dump($result->isValid()); //    $data['name'] = 'Pi'; $data['home'] = 'Maple'; $result = $validator->validate($data); var_dump($result->isValid()); //   foreach($result->errors() as $error) { echo $error."\n"; } foreach($result->invalidFields() as $fieldResult) { echo $fieldResult->path().":\n"; foreach($fieldResult->errors() as $error) { echo $error."\n"; } } /* bool(true) bool(false) Fairies live only inside oaks name: Value did not pass filter 'minLength' */ 


Work with results



As you can see above, the result directly includes its errors and also the results of all nested fields. It may seem that the errors themselves are just text strings, but in fact they are classes implementing the __toString () magic method for convenience only. When working with forms, you will almost never show this default text to the user. Instead, get its type and parameters from the error class and then format it nicely, for example:

 if($error->type() === 'filter') { if($error->filter() === 'minLength') { $params = $error->parameters(); echo "Please enter at least {$params[0]} characters"; } } 


Thus, a small helper class can make a beautiful localization of various types of errors.

Data structures


Well, that's actually a killer feature, let's try to validate this structure:

 $data = array( 'name' => 'Pixie', // 'home'    'home' => array( 'location' => 'forest', 'name' => 'Oak' ), // 'spells'    , //    (   ) // of the same type 'spells' => array( 'charm' => array( 'name' => 'Charm Person', 'type' => 'illusion' ), 'blast' => array( 'name' => 'Fire Blast', 'type' => 'evocation' ), // .... ) ); $validator = $validate->validator(); $document = $validator->rule()->addDocument(); $document->valueField('name') ->required() ->addFilter() ->alpha() ->minLength(3); //  $homeDocument = $document->valueField('home') ->required() ->addDocument(); $homeDocument->valueField('location') ->required() ->addFilter() ->in(array('forest', 'meadow')); $homeDocument->valueField('name') ->required() ->addFilter() ->alpha(); //   $spellsArray = $document->valueField('spells') ->required() ->addArrayOf() ->minCount(1); //    $spellDocument = $spellsArray ->valueKey() ->filter('alpha'); //     $spellDocument = $spellsArray ->valueItem() ->addDocument(); $spellDocument->valueField('name') ->required() ->addFilter() ->minLength(3); $spellDocument->valueField('type') ->required() ->addFilter() ->alpha(); 


Same using alternate syntax
 $validator = $validate->validator(function($value) { $value->document(function($document) { $document ->field('name', function($name) { $name ->required() ->filter(array( 'alpha', 'minLength' => array(3) )); }) ->field('home', function($home) { $home ->required() ->document(function($home) { $home->field('location', function($location) { $location ->required() ->addFilter() ->in(array('forest', 'meadow')); }); $home->field('name', function($name) { $name ->required() ->filter('alpha'); }); }); }) ->field('spells', function($spells) { $spells->required()->arrayOf(function($spells){ $spells ->minCount(1) ->key(function($key) { $key->filter('alpha'); }) ->item(function($spell) { $spell->required()->document(function($spell) { $spell->field('name', function($name) { $name ->required() ->addFilter() ->minLength(3); }); $spell->field('type', function($type) { $type ->required() ->filter('alpha'); }); }); }); }); }); }); }); 



The alternative syntax in my opinion is much more readable in this case, since the tabulation of the code coincides with the tabulation of the document.

Look at the results

 $result = $validator->validate($data); var_dump($result->isValid()); //bool(true) //   $data['name'] = ''; $data['spells']['charm']['name'] = '1'; //    $data['spells'][3] = $data['spells']['blast']; $result = $validator->validate($data); var_dump($result->isValid()); //bool(false) //      function printErrors($result) { foreach($result->errors() as $error) { echo $result->path().': '.$error."\n"; } foreach($result->invalidFields() as $result) { printErrors($result); } } printErrors($result); /* name: Value is empty spells.charm.name: Value did not pass filter 'minLength' spells.3: Value did not pass filter 'alpha' */ 


Demo


To try your own hands on Validate enough:

 git clone https://github.com/phpixie/validate cd validate/examples #      curl -sS https://getcomposer.org/installer | php php composer.phar install php simple.php php document.php 


And by the way, like all other libraries from PHPixie, you will be 100% covered in code by tests and work under any version of PHP older than 5.3 (including the new 7 and HHVM).

Source: https://habr.com/ru/post/269709/


All Articles