Правила валидации Laravel 13: справочник
Laravel содержит десятки встроенных правил валидации, и запомнить, чем одно отличается от другого, с первого раза непросто. В этом справочнике разобраны ключевые правила с примерами и рекомендациями по выбору между похожими правилами. Правила, для которых есть отдельная статья с детальным разбором, описаны кратко со ссылкой.
Три способа задать правила
Прежде чем перейти к конкретным правилам, разберёмся с синтаксисом. Laravel позволяет записывать правила тремя способами, и выбор между ними влияет на читаемость кода.
Строковый синтаксис через вертикальную черту – самый компактный вариант:
$request->validate([
'title' => 'required|string|max:200',
'category_id' => 'required|integer|exists:categories,id',
]);
Массив правил – обязательный формат, если одно из правил содержит запятые или использует объект Rule:
use Illuminate\Validation\Rule;
$request->validate([
'email' => ['required', 'email:rfc,dns', Rule::unique('users')->ignore($user->id)],
'role' => ['required', Rule::in(['editor', 'viewer'])],
]);
Fluent-билдер через класс Rule – появился в Laravel 11 (Rule::date(), Rule::string() и другие). Позволяет собирать сложные правила цепочкой вызовов:
use Illuminate\Validation\Rule;
$request->validate([
'username' => [
'required',
Rule::string()->min(3)->max(30)->alphaDash(ascii: true),
],
'start_date' => [
'required',
Rule::date()->afterToday(),
],
]);
Все три способа можно комбинировать в рамках одного вызова validate. Строковый формат подходит для простых правил, массивы – для составных, а fluent-билдер – когда нужна условная логика через when().
Обязательность и присутствие полей
Эта группа правил отвечает за то, должно ли поле существовать в запросе и может ли оно быть пустым. Путаница между ними – одна из самых частых проблем при валидации.
required
Поле должно присутствовать в данных и не быть пустым. Пустыми считаются: null, пустая строка "", пустой массив (или пустой Countable-объект), загруженный файл без пути.
$request->validate([
'name' => 'required|string|max:100',
'items' => 'required|array|min:1',
]);
filled
Если поле присутствует в запросе – оно не должно быть пустым. Но если поля нет вообще – валидация пройдёт. Правило полезно для API, где клиент может не отправлять поле, но если отправил – значение обязательно.
$request->validate([
'nickname' => 'filled|string|max:50',
]);
// { } – ok (поля нет)
// { "nickname": "alex" } – ok
// { "nickname": "" } – ошибка
present
Поле обязано быть в запросе, но может содержать что угодно, включая null и пустую строку. Используется, когда фронтенд должен явно передать ключ, даже если значение пустое.
$request->validate([
'comment' => 'present|nullable|string',
]);
// { "comment": null } – ok
// { "comment": "" } – ok
// { } – ошибка (ключ отсутствует)
У present есть условные варианты – present_if, present_unless, present_with, present_with_all – для случаев, когда обязательность ключа зависит от других полей.
nullable
Разрешает значение null. Без этого правила null провалит проверку любым типовым правилом вроде string или integer.
$request->validate([
'middle_name' => 'nullable|string|max:50',
'deleted_at' => 'nullable|date',
]);
Частое заблуждение: nullable не делает поле необязательным. Если перед nullable стоит required – поле обязано быть в запросе, но может прийти как null.
sometimes
Правило sometimes говорит валидатору: проверяй остальные правила только если поле реально присутствует в данных. Если ключа нет – пропусти его целиком.
$validator = Validator::make($data, [
'phone' => 'sometimes|required|string|min:10',
]);
В этом примере, если ключ phone отсутствует в $data, ни одно правило для него не сработает. Но если ключ есть – дальше идёт required, и пустая строка не пройдёт.
sometimes vs nullable – в чём разница
Это один из самых задаваемых вопросов. Разница принципиальная:
sometimes– условие на уровне присутствия ключа. Нет ключа – нет проверкиnullable– условие на уровне значения. Ключ есть, значениеnull– ok
// Поле можно не отправлять. Если отправили – проверяем формат
'bio' => 'sometimes|nullable|string|max:1000',
// Поле обязательно, но может быть null
'supervisor_id' => 'required|nullable|integer|exists:users,id',
Первый вариант (sometimes|nullable) подходит для PATCH-запросов, где клиент отправляет только изменённые поля. Второй – для полей вроде “руководитель”, которые всегда должны быть в запросе, но у кого-то руководителя нет.
Необязательные поля
В Laravel нет отдельного правила optional. Необязательность поля достигается комбинацией sometimes|nullable. Middleware ConvertEmptyStringsToNull (включён по умолчанию) превращает пустые строки в null, поэтому nullable обычно нужен вместе с sometimes:
$request->validate([
'website' => 'sometimes|nullable|url',
'notes' => 'sometimes|nullable|string|max:2000',
]);
Поля, не перечисленные в правилах вообще, не валидируются и не попадают в результат validated(). Если поле нужно в результате, но оно необязательное – его придётся явно прописать с sometimes|nullable.
missing
Обратное present – ключ обязан отсутствовать в данных. Полезно для API-версионирования или когда поле удалено из формы, а клиент продолжает его отправлять:
$request->validate([
'legacy_field' => 'missing',
]);
Условные варианты – missing_if, missing_unless, missing_with, missing_with_all – позволяют требовать отсутствие ключа при определённых условиях.
bail и остановка при первой ошибке
По умолчанию Laravel проверяет все правила для каждого поля и собирает полный список ошибок. Правило bail меняет поведение: при первой же ошибке в правилах данного поля проверка останавливается.
$request->validate([
'email' => 'bail|required|email:rfc|unique:users',
'password' => 'required|min:8',
]);
Здесь, если email не прошёл проверку required, правила email:rfc и unique:users не выполнятся. Для password же проверка всё равно пройдёт полностью – bail действует только на своё поле.
По соглашению bail ставят первым в списке правил для читаемости – сразу видно, что поле остановится при первой ошибке.
Зачем это нужно на практике: правило unique выполняет SQL-запрос. Если поле вообще пустое, нет смысла лезть в базу – bail предотвращает лишний запрос.
Когда bail мешает: если форма должна показать все ошибки разом (стандартное поведение), bail ухудшит UX – пользователь будет исправлять ошибки по одной. Используйте bail точечно для тяжёлых правил, а не для каждого поля.
Для глобальной остановки на уровне всех полей существует метод stopOnFirstFailure:
$validator = Validator::make($data, $rules);
if ($validator->stopOnFirstFailure()->fails()) {
// только первая ошибка первого проваленного поля
}
Ограничение допустимых значений
in и not_in
Правило in проверяет, что значение входит в заданный список. Это основной инструмент для полей, где допустим только конкретный набор вариантов.
$request->validate([
'status' => 'required|in:draft,published,archived',
'priority' => ['required', Rule::in([1, 2, 3, 4, 5])],
]);
Есть нюанс с типами. Правило in при строковом синтаксисе (in:1,2,3) сравнивает как строки. Если нужна проверка типов, передавайте числа через Rule::in, а не через строку:
// '1' пройдёт, потому что 'in:1,2' сравнивает строки
'level' => 'required|in:1,2,3',
// здесь '1' тоже пройдёт – Rule::in сравнивает нестрого
'level' => ['required', Rule::in([1, 2, 3])],
Если критична строгость типов, добавьте integer или numeric перед in – тогда до проверки in значение уже будет гарантированно числовым.
not_in работает наоборот – значение не должно входить в список:
$request->validate([
'username' => ['required', 'string', Rule::notIn(['admin', 'root', 'system'])],
]);
enum
Для PHP-перечислений (backed enums) есть отдельное правило Rule::enum. Оно проверяет, что входное значение соответствует одному из значений enum:
enum OrderStatus: string
{
case Pending = 'pending';
case Paid = 'paid';
case Shipped = 'shipped';
case Cancelled = 'cancelled';
}
$request->validate([
'status' => ['required', Rule::enum(OrderStatus::class)],
]);
Методы only и except ограничивают допустимые варианты:
// Клиент может выбрать только эти статусы
Rule::enum(OrderStatus::class)->only([
OrderStatus::Pending,
OrderStatus::Cancelled,
]);
// Все, кроме отменённого
Rule::enum(OrderStatus::class)->except([OrderStatus::Cancelled]);
Условная логика через when:
Rule::enum(OrderStatus::class)->when(
$user->isManager(),
fn ($rule) => $rule->only([OrderStatus::Shipped, OrderStatus::Cancelled]),
fn ($rule) => $rule->except([OrderStatus::Shipped]),
);
Если в проекте нет backed enum, а значения хранятся в конфиге или БД, используйте Rule::in вместо Rule::enum – они решают одну задачу разными способами.
accepted и declined
Правило accepted проверяет, что значение – одно из: "yes", "on", 1, "1", true, "true". Предназначено для чекбоксов согласия с условиями:
$request->validate([
'terms' => 'accepted',
'privacy_policy' => 'accepted',
]);
declined – обратное: "no", "off", 0, "0", false, "false". Обе формы имеют условные варианты: accepted_if:field,value и declined_if:field,value.
boolean
Проверяет, что значение приводится к булевому: true, false, 1, 0, "1", "0". Строковый параметр strict (документирован начиная с Laravel 11) допускает только true и false:
$request->validate([
'is_active' => 'boolean',
'notifications' => 'boolean:strict',
]);
Подробнее про валидацию булевых значений и чекбоксов – в статье про валидацию строк и чисел.
Сравнение полей и подтверждение
confirmed
Правило confirmed ожидает, что в данных есть второе поле с суффиксом _confirmation. Типичное применение – подтверждение пароля или email:
$request->validate([
'password' => 'required|min:8|confirmed',
]);
// в запросе должно быть поле password_confirmation
Можно указать другое имя поля для подтверждения:
$request->validate([
'email' => 'required|email|confirmed:email_repeat',
]);
// сравнивает email с полем email_repeat
same и different
Правило same проверяет совпадение значений двух полей, different – наоборот:
$request->validate([
'new_password' => 'required|min:8|different:current_password',
'new_password_confirmation' => 'required|same:new_password',
]);
На первый взгляд same и confirmed делают одно и то же, но механика отличается:
// confirmed: правило на исходном поле, суффикс _confirmation подразумевается
'password' => 'required|confirmed',
// Laravel сам найдёт password_confirmation и сравнит
// same: правило на втором поле, имя первого указывается явно
'password_repeat' => 'required|same:password',
// сравнивает password_repeat с password
confirmed удобнее для стандартных форм с суффиксом _confirmation. same нужен, когда имена полей произвольные.
Проверка на точное значение
Для проверки, что поле равно конкретному значению, Laravel не имеет отдельного правила equals. Вместо этого используется in с одним значением или same для сравнения с другим полем:
$request->validate([
// значение должно быть ровно "premium"
'plan' => ['required', Rule::in(['premium'])],
// числовое значение равно 100 (size для integer – это значение, не длина)
'percentage' => 'required|integer|size:100',
]);
Правило size для числовых полей проверяет точное соответствие значения, а не длину. С правилом integer запись size:10 означает “значение равно 10”.
between
Правило between проверяет, что значение попадает в диапазон (включительно). Поведение зависит от типа поля:
$request->validate([
'age' => 'required|integer|between:18,120',
'title' => 'required|string|between:5,200',
'attachments' => 'array|between:1,5',
'avatar' => 'file|between:10,2048',
]);
Для чисел – сравнивается значение, для строк – длина в символах, для массивов – количество элементов, для файлов – размер в килобайтах. Детальнее – в статье про валидацию строк и чисел.
Строковые и символьные правила
Правило alpha по умолчанию работает с Unicode – кириллица, иероглифы и диакритические знаки пройдут проверку. Если нужна только латиница, укажите параметр ascii:
$request->validate([
// "Иван" пройдёт – Unicode-буквы допустимы
'first_name' => 'required|alpha',
// "Иван" не пройдёт – только a-z, A-Z
'username' => 'required|alpha_dash:ascii',
// буквы + цифры, строго 6 символов
'code' => 'required|alpha_num|size:6',
]);
Семейство alpha не допускает пробелов. Для имён вроде “Анна Мария” alpha не подходит – используйте regex или string с ограничением длины.
alpha_dash расширяет alpha дефисом и подчёркиванием (но не точкой и не пробелом). alpha_num допускает буквы и цифры без спецсимволов.
Отдельно про regex – частый источник ошибок. Если регулярное выражение содержит символ |, строковый синтаксис через вертикальную черту сломается, потому что Laravel воспримет | как разделитель правил:
// Сломается: Laravel распарсит "regex:/^(foo" как одно правило, "bar)$/" как другое
'code' => 'required|regex:/^(foo|bar)$/',
// Правильно: массив правил, где regex – отдельный элемент
'code' => ['required', 'regex:/^(foo|bar)$/'],
Это касается и not_regex. Если в паттерне есть | – только массив.
Полный список строковых правил – string, email, url, uuid, regex, lowercase, uppercase, starts_with, ends_with – описан в справочнике по валидации строк и чисел.
Правила для массивов
Правило distinct гарантирует отсутствие дубликатов в массиве. Типичный сценарий – форма, где пользователь добавляет несколько email-адресов для рассылки:
$request->validate([
'recipients' => 'required|array|min:1|max:20',
'recipients.*.email' => 'required|email|distinct',
'recipients.*.role' => 'required|in:to,cc,bcc',
]);
Без distinct два одинаковых email пройдут валидацию, и письмо уйдёт дважды.
По умолчанию distinct использует нестрогое сравнение. Параметр strict включает строгую проверку (полезно для массива ID, где "1" и 1 – разные значения), а ignore_case игнорирует регистр:
'options.*.id' => 'distinct:strict',
'categories.*' => 'distinct:ignore_case',
Правила array, list, in_array, contains, required_array_keys и валидация вложенных структур через wildcard * подробно описаны в статье про валидацию массивов и JSON.
prohibited и запрет значений
Правило prohibited требует, чтобы поле было пустым или отсутствовало. Пустым считаются: null, пустая строка, пустой массив, файл без пути.
$request->validate([
'role' => 'prohibited',
]);
На практике чаще используются условные варианты. prohibited_if запрещает поле, когда другое поле имеет определённое значение:
$request->validate([
'is_company' => 'required|boolean',
'company_name' => 'prohibited_if:is_company,false',
'personal_id' => 'prohibited_if:is_company,true',
]);
prohibited_unless работает наоборот – поле запрещено, если условие не выполнено. prohibits запрещает другие поля при наличии текущего:
$request->validate([
// если задан custom_message – template_id запрещён
'custom_message' => 'sometimes|string|max:500|prohibits:template_id',
'template_id' => 'required_without:custom_message|integer|exists:templates,id',
]);
Важно: prohibits использует ту же концепцию «пустоты», что и required (null, "", [], файл с пустым путём). Логическое false под это правило не попадает, поэтому 'flag' => 'sometimes|boolean|prohibits:other' забанит other даже на flag: false, что обычно не то, что задумано. Для булевых флагов используйте prohibited_if:flag,true или варианты для чекбоксов, которые описаны ниже.
Для чекбоксов удобны prohibited_if_accepted и prohibited_if_declined – они не требуют перечисления значений, а ориентируются на “включённость” поля:
$request->validate([
'use_default_address' => 'required|boolean',
// если чекбокс включён – кастомный адрес запрещён
'custom_address' => 'prohibited_if_accepted:use_default_address|string|max:500',
]);
Для сложной логики подходит Rule::prohibitedIf с замыканием:
$request->validate([
'discount_code' => Rule::prohibitedIf(fn () => $order->isPaid()),
]);
Условные правила: зависимость от других полей
Правила required_if, required_with, required_without и другие делают поле обязательным в зависимости от значений соседних полей. Это механизм для форм, где набор полей меняется динамически.
$request->validate([
'delivery_type' => 'required|in:pickup,courier',
// адрес обязателен при курьерской доставке
'address' => 'required_if:delivery_type,courier|string|max:500',
// время самовывоза обязательно, если выбран самовывоз
'pickup_time' => 'required_if:delivery_type,pickup|date',
]);
required_with делает поле обязательным, если любое из перечисленных полей присутствует:
$request->validate([
'latitude' => 'required_with:longitude|numeric|between:-90,90',
'longitude' => 'required_with:latitude|numeric|between:-180,180',
]);
Детальный разбор всех условных правил – required_if, required_unless, required_with, required_with_all, required_without, required_without_all, а также метод $validator->sometimes() для программной логики – в статье про условную валидацию.
anyOf: OR-логика в правилах
Метод Rule::anyOf позволяет задать альтернативные наборы правил. Поле считается валидным, если проходит хотя бы один из вариантов:
$request->validate([
'contact' => [
'required',
Rule::anyOf([
['email'],
['string', 'regex:/^\+\d{10,15}$/'],
]),
],
]);
В этом примере поле contact может быть либо email-адресом, либо телефонным номером в международном формате. Без anyOf пришлось бы писать кастомное правило или разделять поле на два.
Ещё пример – поле, которое принимает либо UUID, либо числовой ID:
$request->validate([
'reference' => [
'required',
Rule::anyOf([
['uuid'],
['integer', 'min:1'],
]),
],
]);
Валидация по базе данных
Правила unique и exists выполняют SQL-запросы для проверки значений. Оба поддерживают fluent-синтаксис через Rule::unique и Rule::exists с фильтрами, мягким удалением и ссылками на Eloquent-модели.
Базовый пример:
$request->validate([
'email' => ['required', 'email', Rule::unique('users')->ignore($user->id)],
'category_id' => ['required', Rule::exists('categories', 'id')->where('active', true)],
]);
Подробный разбор с мультиколоночной уникальностью, soft deletes, кастомным соединением и продвинутыми where-условиями – в статье про unique и exists.
Порядок правил и их взаимодействие
Правила выполняются слева направо. Это важно, потому что некоторые правила останавливают цепочку.
nullable – если значение null, дальнейшие правила не проверяются. Поле считается прошедшим валидацию:
// null пройдёт, и email:rfc не выполнится
'contact_email' => 'nullable|email:rfc|unique:users',
sometimes работает ещё раньше – если ключа нет в данных, вся цепочка пропускается. Это не то же самое, что “правило не сработало” – поле просто не участвует в валидации и не попадает в validated().
Типовое правило (string, integer, array) определяет, как будут работать min, max, size, between. Без типового правила Laravel попробует угадать тип из входных данных, что приводит к неожиданностям:
// min:3 проверит длину строки (>= 3 символов)
'name' => 'required|string|min:3',
// min:3 проверит значение числа (>= 3)
'quantity' => 'required|integer|min:3',
// без типового правила – если придёт "10", min:3 проверит длину строки "10" (2 символа – ошибка!)
'ambiguous' => 'required|min:3',
Всегда указывайте типовое правило перед размерными.
Правила по типам данных
Для каждого типа данных есть отдельная статья с полным разбором. Ниже – краткая навигация с подсказкой, какое правило выбрать.
- Строки и числа – если поле текстовое, начинайте с
string. Для чисел:integerдля целых,numericдля дробных,decimal:2для фиксированной точности (цены). Для email используйтеemail:rfc– без параметра проверка слишком мягкая. Полный разбор: Валидация строк и чисел - Даты –
dateпринимает любой форматstrtotime,date_formatтребует конкретный. Для сравнений (after,before) можно ссылаться на другое поле. Fluent-билдерRule::date()удобнее строкового синтаксиса: Валидация дат - Файлы –
fileпроверяет факт загрузки,imageограничивает типами изображений. Для проверки содержимого используйтеmimes, для расширения –extensions. Оба нужны одновременно: Валидация файлов - Массивы и JSON –
arrayдля PHP-массивов,listдля массивов с последовательными ключами (не ассоциативных),jsonдля JSON-строк. Wildcard*незаменим для валидации элементов: Валидация массивов - Пароли –
Password::min(8)->letters()->numbers()– fluent-билдер, комбинируется сuncompromised()для проверки утечек: Валидация паролей
Полная таблица правил
Для быстрого поиска – все встроенные правила, сгруппированные по назначению.
Присутствие и обязательность
| Правило | Назначение |
|---|---|
required | Поле обязательно и не пустое |
filled | Не пустое, если присутствует |
present | Ключ обязан быть в данных |
nullable | Разрешает null |
sometimes | Проверять, только если ключ есть |
missing | Ключ обязан отсутствовать |
prohibited | Поле пусто или отсутствует |
Условные
| Правило | Назначение |
|---|---|
required_if | Обязательно при значении другого поля |
required_unless | Обязательно, если другое поле не равно |
required_with | Обязательно при наличии других полей |
required_without | Обязательно при отсутствии других |
required_if_accepted | Обязательно, если другое поле accepted |
prohibited_if | Запрещено при значении другого поля |
prohibited_unless | Запрещено, кроме определённого случая |
exclude_if | Исключить из validated(), если условие |
exclude_unless | Включить в validated(), если условие |
Тип значения
| Правило | Назначение |
|---|---|
string | Строка |
integer | Целое число |
numeric | Число (включая float) |
boolean | Булево или приводимое к нему |
array | PHP-массив |
list | Массив с последовательными индексами |
json | Валидная JSON-строка |
file | Загруженный файл |
date | Валидная дата |
Сравнение
| Правило | Назначение |
|---|---|
confirmed | Совпадает с полем {field}_confirmation |
same:field | Совпадает с указанным полем |
different:field | Отличается от указанного поля |
gt:field | Больше другого поля |
gte:field | Больше или равно |
lt:field | Меньше другого поля |
lte:field | Меньше или равно |
Ограничение значений
| Правило | Назначение |
|---|---|
in:a,b,c | Одно из перечисленных значений |
not_in:a,b,c | Не входит в список |
Rule::enum() | Значение PHP enum |
accepted | Значение вроде yes/on/true/1 |
declined | Значение вроде no/off/false/0 |
Rule::anyOf() | Проходит хотя бы один набор правил |
Размер и диапазон
| Правило | Назначение |
|---|---|
min:n | Минимум (символов, значения, элементов, КБ) |
max:n | Максимум |
size:n | Точное значение |
between:min,max | Диапазон (включительно) |
digits:n | Точное количество цифр |
digits_between:min,max | Диапазон цифр (legacy, используйте min_digits/max_digits) |
min_digits:n | Минимум цифр |
max_digits:n | Максимум цифр |
Строковые
| Правило | Назначение |
|---|---|
alpha | Только буквы |
alpha_dash | Буквы, цифры, дефис, подчёркивание |
alpha_num | Буквы и цифры |
email | Формат email |
url | Формат URL |
uuid | Формат UUID |
regex:pattern | Соответствие регулярному выражению |
starts_with:a,b | Начинается с одного из значений |
ends_with:a,b | Заканчивается одним из значений |
lowercase | В нижнем регистре |
uppercase | В верхнем регистре |
Управление потоком
| Правило | Назначение |
|---|---|
bail | Остановить проверку поля при первой ошибке |
exclude | Исключить из результата validated() |
Практические советы
Несколько правил, которые упрощают повседневную работу с валидацией.
Для PATCH-запросов, где клиент отправляет только изменённые поля, комбинируйте sometimes с нужными правилами:
// UpdateProfileRequest
public function rules(): array
{
return [
'name' => 'sometimes|required|string|max:100',
'email' => ['sometimes', 'required', 'email', Rule::unique('users')->ignore($this->user()->id)],
'bio' => 'sometimes|nullable|string|max:500',
];
}
Для полей, которые зависят от типа сущности, используйте required_if в сочетании с prohibited_if:
$request->validate([
'type' => 'required|in:individual,company',
'inn' => 'required_if:type,company|prohibited_if:type,individual|string|size:10',
'passport' => 'required_if:type,individual|prohibited_if:type,company|string',
]);
bail разумно ставить перед правилами, выполняющими запросы к БД или внешним сервисам:
$request->validate([
'email' => 'bail|required|email:rfc|unique:users',
'invite_code' => 'bail|required|string|exists:invites,code',
]);
Если правила растут и перестают помещаться в контроллере, переносите их в Form Request. А когда встроенных правил не хватает, создавайте собственные. Работа с текстами ошибок разобрана в отдельном справочнике, а быстрый старт поможет разобраться с основами.