Валидация является важным аспектом взаимодействия каждого приложения с данными. Вместо того, чтобы заново изобретать колесо, сообщество работало над некоторыми полезными пакетами, такими как Symfony, Laravel, Zend и т. Д. В этой статье мы собираемся представить менее известный пакет под названием Respect Validation , который предоставляет несколько приятных новых функций. Давайте начнем.
Популярные пакеты проверки
При использовании фреймворка есть большой шанс, что у вас есть компонент проверки, поставляемый с ним. Компонент Symfony Validator предоставляет набор стандартных правил проверки, которые вам понадобятся для вашего приложения.
class UserSubscriptionForm { protected $email; public static function loadValidatorMetadata(ClassMetadata $metadata) { $metadata->addPropertyConstraint('email', new \Assert\Email([ 'message' => 'Invalid email.' ])); } }
Я уверен, что ваша форма подписки содержит больше, чем просто электронное письмо, но давайте будем проще. Внутри вашего контроллера вы должны запустить проверку следующим образом:
public function store(Request $request){ $userSubscriptionForm = UserSubscriptionForm::fromInput($request->all()); $validator = $this->get('validator'); $errors = $validator->validate($userSubscriptionForm); if (count($errors) > 0) { // redirect with errors } // return success }
После создания новой пользовательской формы UserSubscriptionForm
из пользовательского ввода мы UserSubscriptionForm
проверку и соберем ошибки проверки, если мы их обнаружим.
Laravel также поставляется с пакетом проверки Illuminate . Это делает ту же работу, но по-другому. Давайте использовать наш предыдущий пример проверки и преобразовать его в Laravel.
class UserSubscriptionController extends Controller { public function store(Request $request) { $validator = \Validator::make( [ 'email' => $request->get('email') ], [ 'email' => 'required|email' ] ); if($validator->fails()){ $errors = $validator->messages(); // redirect with errors } // return success } }
Пакет валидатора Zend мало чем отличается от других. Для того же предыдущего примера мы можем сделать следующее:
class UserSubscriptionController extends Controller { public function store(Request $request) { $validator = new Zend\Validator\EmailAddress(); if(!$validator->isValid($request->get('email'))){ $errors = $validator->getMessages(); // redirect with errors } // return success } }
Я уверен, что вы уже знакомы хотя бы с одним из пакетов, упомянутых выше. В этой статье мы собираемся представить Respect / Validation и выделим основные отличия от других пакетов.
Уважение Валидация
Валидатор Respect представляет простой способ проверки ваших данных. Давайте начнем с того же предыдущего примера.
class UserSubscriptionController extends Controller { public function store(Request $request) { $emailValidator = \Respect\Validation\Validator::email(); if (!$emailValidator->validate($email)) { // redirect with errors } // return success } }
Итак, ничего нового! Первое, что вы заметите, это то, что мы не получили список ошибок после сбоя проверки. Чтобы получить список ошибок, нам нужно использовать метод assert
который выдает исключение, содержащее список сообщений об ошибках.
class UserSubscriptionController extends Controller { public function store(Request $request) { $emailValidator = \Respect\Validation\Validator::email(); try{ $emailValidator->assert($request->get('email')); } catch(\Respect\Validation\Exceptions\NestedValidationExceptionInterface $ex){ $errors = $ex->getMainMessage(); // redirect with errors } // return success } }
У нас также есть возможность добавлять несколько правил к одному и тому же значению с помощью цепочки методов.
Validator::string()->noWhitespace()->length(4, 10); Validator::numeric()->between(5, 10); Validator::date()->between(5, 10);
Еще один способ проверки нескольких правил — использовать правило allOf
которое принимает список правил.
$inputValidator = \Respect\Validation\Validator::allOf( new String(), new Length(4, 10) );
Возможно, вам необходимо проверить некоторые данные, чтобы соответствовать хотя бы одному из ваших бизнес-правил. Давайте возьмем форму входа в систему в качестве примера, где пользователь может либо ввести свой адрес электронной почты или имя пользователя. Имя пользователя должно быть буквенно-цифровым и должно содержать от 4 до 16 символов. Вы можете проверить документацию для получения более подробной информации о списке доступных правил.
$usernameOrEmailValidator = \Respect\Validation\Validator::oneOf( new \Respect\Validation\Rules\Email(), \Respect\Validation\Validator::string()->alnum()->noWhitespace()->length(4, 16) );
Одна особенность, которую вы не часто видите в пакетах проверки, — это функция отрицания правил. Например, вы можете указать правила, которым вы не хотите соответствовать.
$inputValidator = \Respect\Validation\Validator::not(\Respect\Validation\Validator::numeric());
Пользовательские сообщения об ошибках
Как упоминалось ранее, при возникновении исключения утверждения вы можете получать сообщения об ошибках одним из следующих способов.
-
getFullMessage
: возвращает общее сообщение об ошибке со списком ошибочных правил. AssertingValidator::email()->assert('notAValidEmail')
выдаст следующее сообщение.
\-These rules must pass for "notAValidEmail" \-"notAValidEmail" must be valid email
-
getMainMessage
: возвращает общее сообщение об ошибке без указания ошибочных правил. Пример электронной почты возвращаетThese rules must pass for "notAValidEmail
. -
findMessages
: принимает массив как параметр, содержащий список сообщений для ошибочных правил.
$this->messages = [ 'alpha' => '{{name}} must only contain alphabetic characters.', 'alnum' => '{{name}} must only contain alpha numeric characters and dashes.', 'numeric' => '{{name}} must only contain numeric characters.', 'noWhitespace' => '{{name}} must not contain white spaces.', 'length' => '{{name}} must length between {{minValue}} and {{maxValue}}.', 'email' => 'Please make sure you typed a correct email address.', 'creditCard' => 'Please make sure you typed a valid card number.', 'date' => 'Make sure you typed a valid date for the {{name}} ({{format}}).', 'password_confirmation' => 'Password confirmation doesn\'t match.' ]; // inside the try catch block try { $this->rules[$input]->assert($value); } catch (\Respect\Validation\Exceptions\NestedValidationExceptionInterface $ex) { dump($ex->findMessages($this->messages)); }
Это вернет пользовательское сообщение, если в нашем массиве сообщений существует одно из правил. Вы можете прочитать больше о пользовательских сообщениях в документации .
Чтобы сделать все немного более практичным, мы будем проверять форму подписки пользователя. Форма содержит набор полей для идентификации пользователя и небольшой раздел для платежной информации.
Проверка подписки пользователя
Я буду использовать Laravel для моего примера, но я думаю, что в этом случае это не имеет значения, поскольку мы не используем магию Laravel. Вы можете начать с добавления пакета, используя composer: composer require respect/validation
. Форма подписки пользователя будет выглядеть так:
// resources/views/home.blade.php <form action="/send" method="POST"> <legend>Profile</legend> <input class="form-control" name="username" type="text" autofocus="" value="{{\Input::old('username')}}"> <input class="form-control" name="email" type="email" value="{{\Input::old('email')}}"> <input class="form-control" name="password" type="password" value=""> <input class="form-control" name="password_confirmation" type="password" > <legend>Billing</legend> <input class='form-control' size='4' type='text' name="cardHolderName" value="{{\Input::old('cardHolderName')}}"> <input class='form-control' size='20' type='text' name="cardNumber" value="{{\Input::old('cardNumber')}}"> <input class='form-control' size='20' type='text' name="billingAddress" value="{{\Input::old('billingAddress')}}"> <input class='form-control' size='4' type='text' name="cvc" value="{{\Input::old('cvc')}}"> <input class='form-control' size='2' type='text' name="expirationMonth" value="{{\Input::old('expirationMonth')}}"> <input class='form-control' size='4' type='text' name="expirationYear" value="{{\Input::old('expirationYear')}}"> <button class="btn btn-primary">Join</button> </form>
// app/Http/routes.php Route::get('/', 'UserSubscriptionController@index'); Route::post('/send', 'UserSubscriptionController@send');
Наш класс UserSubscriptionController
будет иметь два метода. Один для печати формы и другой для обработки отправки формы. Мы создадим еще один отдельный класс для обработки проверки формы.
// app/Http/Controllers/UserSubscriptionController class UserSubscriptionController extends Controller { protected $userSubscription; public function __construct(UserSubscriptionValidator $userSubscription) { $this->userSubscription = $userSubscription; } public function index() { return view('home'); } public function send(Request $request, Redirector $redirector) { $inputs = $request->all(); $isValid = $this->userSubscription->assert($inputs); if (!$isValid) { return $redirector->back()->withInput()->with('errors', $this->userSubscription->errors()); } return "Everything is Good!"; } }
Класс App\Validation\UserSubscriptionValidator
будет App\Validation\UserSubscriptionValidator
через два метода ( assert
и errors
).
// app/Validation/UserSubscriptionValidator.php use \Respect\Validation\Validator as V; class UserSubscriptionValidator { /** * List of constraints * * @var array */ protected $rules = []; /** * List of customized messages * * @var array */ protected $messages = []; /** * List of returned errors in case of a failing assertion * * @var array */ protected $errors = []; /** * Just another constructor * * @return void */ public function __construct() { $this->initRules(); $this->initMessages(); } /** * Set the user subscription constraints * * @return void */ public function initRules() { $dateFormat = 'dm-Y'; $now = (new \DateTime())->format($dateFormat); $tenYears = (new \DateTime('+10 years'))->format($dateFormat); $this->rules['username'] = V::alnum('_')->noWhitespace()->length(4, 20)->setName('Username'); $this->rules['password'] = V::alnum()->noWhitespace()->length(4, 20)->setName('password'); $this->rules['email'] = V::email(); $this->rules['cardHolderName'] = V::alpha()->setName('Card holder name'); $this->rules['cardNumber'] = V::creditCard()->setName('card number'); $this->rules['billingAddress'] = V::string()->length(6)->setName('billing address'); $this->rules['cvc'] = V::numeric()->length(3, 4)->setName('CVC'); $this->rules['expirationDate'] = V::date($dateFormat)->between($now, $tenYears)->setName('expiration date'); } }
Наш конструктор вызывает метод initRules
который устанавливает ограничения валидации. setName
метода setName
вводится в шаблон сообщений об ошибках. Метод initMessages
устанавливает список настраиваемых сообщений; это может быть перемещено за пределы класса, если вы собираетесь использовать их снова для другой формы.
// app/Validation/UserSubscriptionValidator.php /** * Set user custom error messages * * @return void */ public function initMessages() { $this->messages = [ 'alpha' => '{{name}} must only contain alphabetic characters.', 'alnum' => '{{name}} must only contain alpha numeric characters and dashes.', 'numeric' => '{{name}} must only contain numeric characters.', 'noWhitespace' => '{{name}} must not contain white spaces.', 'length' => '{{name}} must length between {{minValue}} and {{maxValue}}.', 'email' => 'Please make sure you typed a correct email address.', 'creditCard' => 'Please make sure you typed a valid card number.', 'date' => 'Make sure you typed a valid date for the {{name}} ({{format}}).', 'password_confirmation' => 'Password confirmation doesn\'t match.' ]; }
Тег {{name}}
будет обновлен соответствующим именем, переданным ранее с использованием метода setName
. Некоторые правила содержат больше переменных, таких как {{minValue}}
и {{maxValue}}
для правила length
. То же самое для правила date
. Следующим шагом является определение нашего метода assert
для фактической проверки.
/** * Assert validation rules. * * @param array $inputs * The inputs to validate. * @return boolean * True on success; otherwise, false. */ public function assert(array $inputs) { $expirationMonth = array_get($inputs, 'expirationMonth'); $expirationYear = array_get($inputs, 'expirationYear'); $inputs['expirationDate'] = '01-' . $expirationMonth . '-' . $expirationYear; foreach ($this->rules as $rule => $validator) { try { $validator->assert(array_get($inputs, $rule)); } catch (\Respect\Validation\Exceptions\NestedValidationExceptionInterface $ex) { $this->errors = $ex->findMessages($this->messages); return false; } } $passwordConfirmed = $this->assertPasswordConfirmation($inputs); return $passwordConfirmed; }
Первым шагом является создание действительной даты из месяца и года окончания срока действия. Это поможет нам легко проверить его по фактическим значениям даты. Мы перебираем список правил валидации и проверяем, соответствует ли пользовательский ввод определенному правилу. Если утверждение не выполняется, мы устанавливаем атрибут сообщения об ошибках и возвращаем контроллеру значение false
.
В настоящее время пакет не предоставляет правила для автоматической проверки подтверждения пароля. Метод assertPasswordConfirmation
будет проверять только равные переданные значения и устанавливать сообщение об ошибке в случае сбоя.
public function assertPasswordConfirmation(array $inputs) { $passwordConfirmation = array_get($inputs, 'password_confirmation'); if ($inputs['password'] !== $passwordConfirmation) { $this->errors['password_confirmation'] = $this->messages['password_confirmation']; return false; } else { return true; } }
Последняя часть состоит в том, чтобы вернуть ошибки пользователю в случае сбоя, и нам также необходимо обновить наш home
шаблон для печати ошибок в случае сбоя.
public function errors() { return $this->errors; }
// home.blade.php @if(\Session::has('errors')) <div class="alert alert-danger"> @foreach(\Session::get('errors') as $error) @if(!empty($error)) <p>{{$error}}</p> @endif @endforeach </div> @endif
Создание ваших собственных правил
Вы можете создать свои собственные бизнес-правила и использовать метод with
чтобы добавить свое пространство имен в массив поиска. Вы можете проверить документацию для более подробной информации о создании ваших собственных ограничений.
Respect\Validation\Validator::with('App\\Validation\\User\\Rules\\');
Если вы обнаружили, что правила, которые вы пытаетесь использовать, существуют в пакете проверки Zend или Symfony, вы можете напрямую использовать их одним из следующих способов.
Respect\Validation\Validator::sf('Language')->validate('Arabic'); Respect\Validation\Validator::zend('EmailAddress')->validate('[email protected]');
Вывод
В этой статье кратко представлен пакет проверки Respect. Мы увидели, насколько простой API и как он интегрируется с другими валидаторами, такими как Symfony и Zend. Также легко расширять и добавлять новые правила в процесс проверки. Если вы никогда не использовали его раньше, я настоятельно рекомендую вам попробовать. Если вы уже использовали его в некоторых своих проектах, мне бы очень хотелось узнать, что вы об этом думаете и как он подходит к вашему варианту использования в долгосрочной перспективе — сообщите нам об этом в комментариях ниже!