Сегодня вышел еще один компонент PHPixie 3, в этот раз для валидации данных. Библиотек для PHP которые занимаются валидацией уже достаточно, зачем тогда писать еще один? На самом деле у большинства из них есть большой недостаток — они работают только с одномерными массивами данных ориентируясь в первую очередь на работу с формами. Такой подход неизбежно устарел в мире API и REST, все чаще приходиться работать с документообразными запросами со сложной структурой. Validate с самого начала был спроектирован как раз чтобы справляться с такими задачами. И даже если вы не используете PHPixie этот компонент может вам очень пригодиться.
Начнем с простого примера, простого одномерного массива:
// Собственно сами данные $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"); } });
$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"); } }); });
И сама валидация:
$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' */
Работа с результатами
Как можно увидеть выше результат включает в себя непосредственно свои ошибки и также результаты всех вложенных полей. Может показаться что сами ошибки это просто текстовые строки, но на самом деле они классы имплементирующие магический метод __toString() только для удобства вывода. При работе с формами вы практически никогда не будете показывать пользователю этот дефолтный текст. Вместо этого получите из класса ошибки ее тип и параметры а затем уже красиво форматируйте, например:
if($error->type() === 'filter') { if($error->filter() === 'minLength') { $params = $error->parameters(); echo "Please enter at least {$params[0]} characters"; } }
Таким образом небольшим хелпер классом можно сделать красивую локализацию различных типов ошибок.
Структуры данных
Ну вот собственно «киллер фича», попробуем провалидировать вот такую структуру:
$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();
$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'); }); }); }); }); }); }); });
Альтернативний синтаксис на мой взгляд гораздо читабельнее в таком случае, так как табуляция кода совпадает с табуляцией документа.
Посмотрим на результаты
$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' */
Демо
Чтобы попробовать Validate своими руками достаточно:
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
И кстати как и у всех других библиотеках от PHPixie вас ждет 100% покрытие кода тестами и работа под любой версией PHP старше 5.3 (включая новую 7 и HHVM).
ссылка на оригинал статьи http://habrahabr.ru/post/269709/
Добавить комментарий