git fbf8495c0fada09ee590502170c5849f09e4cca4
- Использование валидации
- Валидация в контроллере
- Валидация форм
- Работа с сообщениями об ошибках
- Ошибки и шаблоны
- Доступные правила проверки
- Условные правила
- Собственные сообщения об ошибках
- Собственные правила проверки
Laravel поставляется с простой, удобной системой валидации (проверки входных данных на соответствие правилам) и получения сообщений об ошибках - классом Validation
.
$validator = Validator::make(
array('name' => 'Дейл'),
array('name' => 'required|min:5')
);
Первый параметр, передаваемый методу make
- данные для проверки. Второй параметр - правила, которые к ним должны быть применены.
Несколько правил могут быть разделены либо прямой чертой (|), либо быть отдельными элементами массива.
$validator = Validator::make(
array('name' => 'Дейл'),
array('name' => array('required', 'min:5'))
);
$validator = Validator::make(
array(
'name' => 'Дейл',
'password' => 'плохойпароль',
'email' => 'email@example.com'
),
array(
'name' => 'required',
'password' => 'required|min:8',
'email' => 'required|email|unique'
)
);
Как только был создан экземпляр Validator
, метод fails
(или passes
) может быть использован для проведения проверки.
if ($validator->fails())
{
// Переданные данные не прошли проверку
}
Если Validator
нашёл ошибки, вы можете получить его сообщения таким образом:
$messages = $validator->messages();
Вы также можете получить массив правил, данные которые не прошли проверку, без самих сообщений:
$failed = $validator->failed();
Класс Validator
содержит несколько изначальных правил для проверки файлов, такие как size
, mimes
и другие. Для выполнения проверки над файлами просто передайте эти файлы вместе с другими данными.
Laravel после завершения валидации может запустить вашу функцию-замыкание, в которой вы можете, например, проверить что-то особенное или добавить какое-то своё сообщение об ошибке. Для этого служит метод after()
:
$validator = Validator::make(...);
$validator->after(function($validator)
{
if ($this->somethingElseIsInvalid())
{
$validator->errors()->add('field', 'Something is wrong with this field!');
}
});
if ($validator->fails())
{
//
}
Вы можете добавить несколько after
, если это нужно.
Писать полный код валидации каждый раз, когда нужно провалидировать данные - это неудобно. Поэтому Laravel предоставляет несколько решений для упрощения этой процедуры.
Базовый контроллер App\Http\Controllers\Controller
включает в себя трейт ValidatesRequests
, который уже содержит методы для валидации:
/**
* Сохранить пост в блоге.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$this->validate($request, [
'title' => 'required|unique|max:255',
'body' => 'required',
]);
//
}
Если валидация проходит, код продолжает выполняться. Если нет - бросается исключение Illuminate\Contracts\Validation\ValidationException
. Если вы не поймаете это исключение, его поймает фреймворк, заполнит flash-переменные сообщениями об ошибках валидации и средиректит пользователя на предыдущую страницу с формой - сам !
В случае AJAX-запроса редиректа не происходит, фреймворк отдает ответ с HTTP-кодом 422 и JSON с ошибками валидации.
Код, приведенный выше, аналогичен вот этому::
/**
* Сохранить пост в блоге.
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
$v = Validator::make($request->all(), [
'title' => 'required|unique|max:255',
'body' => 'required',
]);
if ($v->fails())
{
return redirect()->back()->withErrors($v->errors());
}
//
}
Если вы хотите кастомизировать сообщения об ошибках валидации, которые сохраняются во флэш-переменных сессии при редиректе, перекройте метод formatValidationErrors
в вашем контроллере:
/**
* {@inheritdoc}
*/
protected function formatValidationErrors(\Illuminate\Validation\Validator $validator)
{
return $validator->errors()->all();
}
Для реализации более сложных сценариев валидации вам могут быть удобны так называемые Form Requests. Это специальные классы HTTP-запроса, содержащие в себе логику валидации. Они обрабатывают запрос до того, как он поступит в контроллер.
Чтобы создать класс запроса, используйте artisan-команду make:request
:
php artisan make:request StoreBlogPostRequest
Класс будет создан в папке app/Http/Requests
. Добавьте необходимые правила валидации в его метод rules
:
/**
* Get the validation rules that apply to the request.
*
* @return array
*/
public function rules()
{
return [
'title' => 'required|unique|max:255',
'body' => 'required',
];
}
Для того, чтобы фрейморк перехватил запрос перед контроллером, добавьте этот класс в аргументы необходимого метода контроллера:
/**
* Сохранить пост в блоге.
*
* @param StoreBlogPostRequest $request
* @return Response
*/
public function store(StoreBlogPostRequest $request)
{
// Валидация успешно пройдена
}
При грамотном использовании валидации запросов вы можете быть уверены, что в ваших контроллерах всегда находятся только отвалидированные входные данные !
В случае неудачной валидации фреймворк заполняет флэш-переменные ошибками валидации и возврашает редирект на предыдущую страницу. В случае AJAX-запроса отдается ответ с кодом 422 и JSON с ошибками валидации.
Классы Form Request также содержать метод authorize
. В этом метоже вы можете проверять, разрешено ли пользователю совершать это действие, обновлять данный ресурс. Например, если пользователь пытается отредактировать комментарий к посту, является ли он его автором ?
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
$commentId = $this->route('comment');
return Comment::where('id', $commentId)
->where('user_id', Auth::id())->exists();
}
Обратите внимание на вызов метода route()
выше. Этод метод дает вам доступ к параметрам в урле (в данном случае это {comment}
), определенным в роуте:
Route::post('comment/{comment}');
Если метод authorize
возвращает false, фреймворк формирует ответ с HTTP-кодом 403 и сразу же отсылает его. Метод контроллера не выполняется.
Если ваша логика авторизации и проверки доступа находится в контроллере, просто верните true
в этом методе:
/**
* Determine if the user is authorized to make this request.
*
* @return bool
*/
public function authorize()
{
return true;
}
Если вы хотите кастомизировать сообщения об ошибках валидации, которые сохраняются во флэш-переменных сессии при редиректе, перекройте метод formatValidationErrors
в базовом классе запросов (App\Http\Requests\Request
):
/**
* {@inheritdoc}
*/
protected function formatValidationErrors(\Illuminate\Validation\Validator $validator)
{
return $validator->errors()->all();
}
После вызова метода messages
объекта Validator
вы получите объект MessageBag
, который имеет набор полезных методов для доступа к сообщеням об ошибках.
echo $messages->first('email');
foreach ($messages->get('email') as $message)
{
//
}
foreach ($messages->all() as $message)
{
//
}
if ($messages->has('email'))
{
//
}
echo $messages->first('email', '<p>:message</p>');
Примечание: по умолчанию сообщения форматируются в вид, который подходит для Twitter Bootstrap.
foreach ($messages->all('<li>:message</li>') as $message)
{
//
}
Как только вы провели проверку, вам понадобится простой способ, чтобы передать ошибки в шаблон. Laravel позволяет удобно сделать это. Например, у нас есть такие роуты:
Route::get('register', function()
{
return View::make('user.register');
});
Route::post('register', function()
{
$rules = array(...);
$validator = Validator::make(Input::all(), $rules);
if ($validator->fails())
{
return redirect('register')->withErrors($validator);
}
});
Заметьте, что когда проверки не пройдены, мы передаём объект Validator
объекту переадресации Redirect
с помощью метода withErrors
. Этот метод сохранит сообщения об ошибках в одноразовых flash-переменных сессии, таким образом делая их доступными для следующего запроса.
Однако, заметьте, мы не передаем в View::make('user.register');
переменные $errors в шаблон. Laravel сам проверяет данные сессии на наличие переменных и автоматически передает их шаблону, если они доступны. Таким образом, важно помнить, что переменная $errors
будет доступна для всех ваших шаблонов всегда, при любом запросе.. Это позволяет вам считать, что переменная $errors
всегда определена и может безопасно использоваться. Переменная $errors
- экземпляр класса MessageBag
.
Таким образом, после переадресации вы можете прибегнуть к автоматически установленной в шаблоне переменной $errors
:
<?php echo $errors->first('email'); ?>
Если у вас есть несколько форм на странице, то вы можете выбрать имя объекта MessageBag, в котором будут возвращаться тексты ошибок, чтобы вы могли их корректно отобразить для нужной формы.
return redirect('register')->withErrors($validator, 'login');
Получить текст ошибки из MessageBag с именем login:
<?php echo $errors->login->first('email'); ?>
Ниже список всех доступных правил и их функции:
- Accepted
- Active URL
- After (Date)
- Alpha
- Alpha Dash
- Alpha Numeric
- Array
- Before (Date)
- Between
- Boolean
- Confirmed
- Date
- Date Format
- Different
- Exists (Database)
- Image (File)
- In
- Integer
- IP Address
- Max
- MIME Types
- Min
- Not In
- Numeric
- Regular Expression
- Required
- Required If
- Required With
- Required With All
- Required Without
- Required Without All
- Same
- Size
- Timezone
- Unique (Database)
- URL
Поле должно быть в значении yes, on или 1. Это полезно для проверки принятия правил и лицензий.
Поле должно быть корректным URL, доступным через функцию checkdnsrr.
Поле должно быть датой, более поздней, чем date. Строки приводятся к датам функцией strtotime
.
Поле можно содержать только алфавитные символы.
Поле можно содержать только алфавитные символы, цифры, знаки подчёркивания (_) и дефисы (-).
Поле можно содержать только алфавитные символы и цифры.
Поле должно быть массивом.
Поле должно быть датой, более ранней, чем date. Строки приводятся к датам функцией strtotime
.
Поле должно быть числом в диапазоне от min до max. Строки, числа и файлы трактуются аналогично правилу size
.
Поле должно быть логическим (булевым). Разрешенные значения: true
, false
, 1
, 0
, "1"
и "0"
.
Значение поля должно соответствовать значению поля с этим именем, плюс foo_confirmation
. Например, если проверяется поле password
, то на вход должно быть передано совпадающее по значению поле password_confirmation
.
Поле должно быть правильной датой в соответствии с функцией strtotime
.
Поле должно подходить под формату даты format в соответствии с функцией date_parse_from_format
.
Значение проверяемого поля должно отличаться от значения поля field.
Поле должно быть корректным адресом e-mail.
Поле должно существовать в заданной таблице базе данных.
Простое использование:
'state' => 'exists:states'
Указание имени поля в таблице:
'state' => 'exists:states,abbreviation'
Вы также можете указать больше условий, которые будут добавлены к запросу "WHERE":
'email' => 'exists:staff,email,account_id,1'
Загруженный файл должен быть изображением в формате jpeg, png, bmp, gif или svg.
Значение поля должно быть одним из перечисленных (foo, bar и т.д.).
Поле должно иметь корректное целочисленное значение.
Поле должно быть корректным IP-адресом.
Значение поля должно быть меньше или равно value. Строки, числа и файлы трактуются аналогично правилу size
.
MIME-тип загруженного файла должен быть одним из перечисленных.
Простое использование:
'photo' => 'mimes:jpeg,bmp,png'
Значение поля должно быть более value. Строки, числа и файлы трактуются аналогично правилу size
.
Значение поля не должно быть одним из перечисленных (foo, bar и т.д.).
Поле должно иметь корректное числовое или дробное значение.
Поле должно соответствовать заданному регулярному выражению.
Внимание: при использовании этого правила может быть нужно перечислять другие правила в виде элементов массива, особенно если выражение содержит символ вертикальной черты (|).
Проверяемое поле должно иметь непустое значение.
Проверяемое поле должно иметь непустое значение, если другое поле field имеет любое из значений value.
Проверяемое поле должно иметь непустое значение, но только если присутствует хотя бы одно из перечисленных полей (foo, bar и т.д.).
Проверяемое поле должно иметь непустое значение, но только если присутствуют все перечисленные поля (foo, bar и т.д.).
Проверяемое поле должно иметь непустое значение, но только если не присутствует хотя бы одно из перечисленных полей (foo, bar и т.д.).
Проверяемое поле должно иметь непустое значение, но только если не присутствуют все перечисленные поля (foo, bar и т.д.).
Поле должно иметь то же значение, что и поле field.
Поле должно иметь совпадающий с value размер. Для строк это обозначает длину, для чисел - число, для файлов - размер в килобайтах.
Поле должно содержать идентификатор часового пояса (таймзоны), один из перечисленных в php-функции timezone_identifiers_list
Значение поля должно быть уникальным в заданной таблице базы данных. Если column
не указано, то будет использовано имя поля.
'email' => 'unique:users'
'email' => 'unique:users,email_address'
'email' => 'unique:users,email_address,10'
Вы также можете указать больше условий, которые будут добавлены к запросу "WHERE"":
'email' => 'unique:users,email_address,NULL,id,account_id,1'
В правиле выше только строки с account_id
равном 1
будут включены в проверку.
Поле должно быть корректным URL.
Примечание: используется PHP-функция
filter_var
Иногда вам нужно валидировать некое поле только тогда, когда оно присутствует во входных данных. Для этого добавьте правило sometimes
:
$v = Validator::make($data, array(
'email' => 'sometimes|required|email',
));
В примере выше поле для поля email
будет запущена валидация только когда $data['email']
существует.
Иногда вам может нужно, чтобы поле имело какое-либо значение только если другое поле имеет значеие, скажем, больше 100. Или вы можете требовать наличия двух полей только, когда также указано третье. Это легко достигается условными правилами. Сперва создайте объект Validator
с набором статичных правил, которые никогда не изменяются:
$v = Validator::make($data, array(
'email' => 'required|email',
'games' => 'required|numeric',
));
Теперь предположим, что ваше приложения написано для коллекционеров игр. Если регистрируется коллекционер с более, чем 100 играми, то мы хотим их спросить, зачем им такое количество. Например, у них может быть магазин или может им просто нравится их собирать. Итак, для добавления такого условного правила мы используем метод Validator
.
$v->sometimes('reason', 'required|max:500', function($input)
{
return $input->games >= 100;
});
Первый параметр этого метода - имя поля, которое мы проверяем. Второй параметр - правило, которое мы хотим добавить, если переданная функция-замыкание (третий параметр) вернёт true
. Этот метод позволяет легко создавать сложные правила проверки ввода. Вы можете даже добавлять одни и те же условные правила для нескольких полей одновременно:
$v->sometimes(array('reason', 'cost'), 'required', function($input)
{
return $input->games >= 100;
});
Примечание: Параметр
$input
, передаваемый замыканию - объектIlluminate\Support\Fluent
и может использоваться для чтения проверяемого ввода и файлов.
Вы можете передать собственные сообщения об ошибках вместо используемых по умолчанию. Если несколько способов это сделать.
$messages = array(
'required' => 'Поле :attribute должно быть заполнено.',
);
$validator = Validator::make($input, $rules, $messages);
Примечание: строка
:attribute
будет заменена на имя проверяемого поля. Вы также можете использовать и другие строки-переменные.
$messages = array(
'same' => 'Значения :attribute и :other должны совпадать.',
'size' => 'Поле :attribute должно быть ровно exactly :size.',
'between' => 'Значение :attribute должно быть от :min и до :max.',
'in' => 'Поле :attribute должно иметь одно из следующих значений: :values',
);
Иногда вам может потребоваться указать своё сообщение для отдельного поля.
$messages = array(
'email.required' => 'Нам нужно знать ваш e-mail адрес!',
);
Также можно определять сообщения валидации в файле локализации вместо того, чтобы передавать их в Validator
напрямую. Для этого добавьте сообщения в массив custom
файла локализации app/lang/xx/validation.php
.
'custom' => array(
'email' => array(
'required' => 'Нам нужно знать ваш e-mail адрес!',
),
),
Laravel изначально содержит множество полезных правил, однако вам может понадобиться создать собственные. Одним из способов зарегистрировать произвольное правило - через метод Validator::extend
.
Validator::extend('foo', function($attribute, $value, $parameters)
{
return $value == 'foo';
});
Примечание: имя правила должно быть в
формате_с_подчёркиваниями
.
Переданная функция-замыкание получает три параметра: имя проверяемого поля $attribute
, значение поля $value
и массив параметров $parameters
переданных правилу.
Вместо функции в метод extend
можно передать ссылку на метод класса:
Validator::extend('foo', 'FooValidator@validate');
Обратите внимание, что вам также понадобится определить сообщение об ошибке для нового правила. Вы можете сделать это либо передавая его в виде массива строк в Validator
, либо вписав в файл локализации.
Вместо использования функций-замыканий для расширения набора доступных правил вы можете расширить сам класс Validator
. Для этого создайте класс, который наследует Illuminate\Validation\Validator
. Вы можете добавить новые методы проверок, начав их имя с validate
.
<?php
class CustomValidator extends Illuminate\Validation\Validator {
public function validateFoo($attribute, $value, $parameters)
{
return $value == 'значение';
}
}
Затем вам нужно зарегистрировать это расширение валидации. Сделать это можно, например, в вашем сервис-провайдере или в ваших старт-файлах.
Validator::resolver(function($translator, $data, $rules, $messages)
{
return new CustomValidator($translator, $data, $rules, $messages);
});
Иногда при создании своего класса валидации вам может понадобиться определить собственные строки-переменные (типа ":foo") для замены в сообщениях об ошибках. Это делается путём создания класса, как было описано выше, и добавлением функций с именами вида replaceXXX
.
protected function replaceFoo($message, $attribute, $rule, $parameters)
{
return str_replace(':foo', $parameters[0], $message);
}
Если вы хотите добавить свое сообщение без использования Validator::extend
, вы можете использовать метод Validator::replacer
:
Validator::replacer('rule', function($message, $attribute, $rule, $parameters)
{
//
});