✅ Валидация
PageBlocks включает гибкую систему валидации для проверки пользовательского ввода. Вы можете определять правила валидации с помощью строки, разделенной символами "|", или в виде массива строк с правилами.
🧩 Валидация в контроллере
Вы можете выполнять валидацию непосредственно в контроллере, используя метод $request->validate():
$request->validate([
'honeypot' => 'empty',
'username' => 'required|string',
'password' => 'required|min:8'
]);Альтернативно, вы можете определить правила в виде массива:
$request->validate([
'honeypot' => ['empty'],
'username' => ['required', 'string'],
'password' => ['required', 'min:8']
]);Оба формата полностью поддерживаются и взаимозаменяемы.
🔁 Формат ответа
Если валидация не проходит, PageBlocks определяет тип запроса:
- Для fetch (XHR) запросов возвращается структурированный JSON-ответ с кодом состояния
422. - Для стандартных (не-XHR) запросов происходит перенаправление назад, а ошибки валидации сохраняются в сессии.
📦 Пример JSON-ответа
{
"success": false,
"message": "The given data was invalid.",
"errors": {
"username": ["The username field is required."],
"password": ["The password must be at least 8 characters."]
}
}🧾 Вывод ошибок в Fenom через сессию
Когда ошибки валидации сохраняются в сессии, вы можете легко отображать их в шаблонах, используя встроенные переменные Fenom:
{$error_message}— содержит общее сообщение об ошибке (например, "The given data was invalid.")
{if $error_message}
<p class="form-message text-center text-error text-danger">{$error_message}</p>
{/if}{$errors}— содержит массив сообщений об ошибках для конкретных полей
<input type="text"
name="username"
class="form-control{if $errors.username} is-invalid{/if}"
>{$old_input}— содержит все отправленные пользователем данные, которые можно использовать для повторного заполнения полей формы после ошибки валидации.
<input type="text" name="name" class="form-control" value="{$old_input.name}">✍️ Пользовательские сообщения об ошибках
PageBlocks позволяет настраивать сообщения об ошибках валидации глобально или для отдельных полей.
🌐 Языковые файлы
Все стандартные сообщения валидации хранятся в:
lang/{lang}/validation.php⚙️ Переопределение сообщений в коде
Вы можете передавать пользовательские сообщения в качестве второго параметра метода validate().
Пользовательское сообщение для всех полей с одинаковым правилом:
$request->validate([
'honeypot' => 'empty',
'username' => 'required|string',
'password' => 'required|string|min:8'
], [
'required' => 'The :attribute field is required.',
]);Сообщения об ошибках для конкретных полей:
$request->validate([
'honeypot' => 'empty',
'username' => 'required|string',
'password' => 'required|string|min:8'
], [
'username.required' => 'We need to know your username!',
]);🏷 Пользовательские имена полей (customAttributes)
Вы можете настроить отображаемые имена ваших полей ввода, передав третий параметр методу validate().
$request->validate([
'username' => 'required|string',
'password' => 'required|min:8',
], [], [
'username' => 'Username',
'password' => 'Password',
]);🧪 Создание валидатора вручную
Хотя $request->validate() является наиболее удобным методом, вы также можете создать валидатор вручную, используя Validator::make():
$validator = Validator::make($request->all(), [
'username' => 'required|string',
'password' => 'required|string|min:8'
]);🚫 Обработка неудачной валидации
if ($validator->fails()) {
return response()->error('', $validator->errors());
}🧼 Доступ к проверенным данным
$validated = $validator->validated();⚙️ За кулисами
new Validation($data, $rules, $messages = [], $customAttributes = []);🔄 Автоматическое перенаправление
Если вы хотите немедленную валидацию с автоматическим перенаправлением в случае неудачи:
Validator::make($request->all(), [
'username' => 'required|string',
'password' => 'required|string|min:8'
])->validate();👜 Использование Error Bags
Иногда на одной странице размещено несколько форм — например, форма входа и форма регистрации. При валидации может возникнуть путаница: ошибки всех форм окажутся в одном массиве errors, и в шаблоне будет сложно понять, к какой форме они относятся.
Чтобы избежать этой проблемы, можно использовать метод validateWithBag($name), который сохраняет ошибки, старые значения и сообщение в именованный мешок (bag) с ключом $name.
🔧 Пример:
Validator::make($request->all(), [
'username' => 'required|string',
'password' => 'required|string|min:8',
])->validateWithBag('login');Этот вызов:
- запишет ошибки в
errors['login']; - передаст старые значения в
old_input['login']; - если задано сообщение, сохранит его в
error_message['login'].
🖼️ Пример в шаблоне
{if errors.login}
<ul class="text-danger">
{foreach $errors.login as $field => $messages}
{foreach $messages as $message}
<li>{$message}</li>
{/foreach}
{/foreach}
</ul>
{/if}
{if error_message.login}
<div class="alert alert-danger">{$error_message.login}</div>
{/if}
<form method="post" action="/login">
<input type="text" name="username" placeholder="Логин"
value="{$old_input.login.username}">
<input type="password" name="password" placeholder="Пароль">
<button type="submit">Войти</button>
</form>Аналогично можно обработать форму регистрации с другим bag:
Validator::make($request->all(), [...])
->validateWithBag('register');{if errors.register}
...
{/if}
{if error_message.register}
...
{/if}
<form method="post" action="/register">
<input type="text" name="email" value="{$old_input.register.email}">
...
</form>🤔 Зачем это нужно?
❌ Без
validateWithBag()все ошибки попадают вerrors, и их нельзя отнести к конкретной форме.✅ С именованным bag ты получаешь:
errors.loginold_input.loginerror_message.login- и можешь точно знать, к какой форме всё это относится.
🧵 Работа с сообщениями об ошибках валидации
📋 Получение всех ошибок
$errors = $validator->errors();🎯 Сообщения для конкретного поля
$messages = $errors->get('email');❗️ Проверка наличия ошибок в поле
if ($errors->has('email')) {
// В поле email есть ошибки валидации
}📚 Доступные правила валидации
🔘 Логические значения
📘 Строки
🔢 Числа
🧺 Массивы
📅 Даты
📂 Файлы
🗄 Базы данных
🧮 Универсальные
🧰 Утилиты
Вот перевод на русский всех правил валидации с сохранением структуры и форматирования:
accepted
Поле должно содержать одно из значений: "yes", "on", 1, "1", true или "true".
'terms' => 'accepted'Используется для проверки подтверждения (например, согласия с условиями через чекбокс).
accepted_if:другое_поле,значение,...
Поле должно быть принято только если другое поле имеет указанное значение.
'newsletter' => 'accepted_if:subscribe,true'Если subscribe равно true, то newsletter должен быть принят (например, чекбокс отмечен).
boolean
Поле должно быть приводимо к логическому типу (true, false, 1, 0, "1", "0").
'agree' => 'boolean'declined
Поле должно содержать одно из значений: "no", "off", 0, "0", false или "false".
'refuse_terms' => 'declined'declined_if:другое_поле,значение,...
Поле должно быть отклонено только если другое поле имеет указанное значение.
'contact_by_phone' => 'declined_if:preferred_contact,email'Если preferred_contact равно email, то contact_by_phone должен быть отклонен.
Строки
string
Значение должно быть строкой. Если допустимо null, используйте nullable|string.
'title' => 'string'email
Значение должно быть корректным email-адресом.
'email' => 'required|email'confirmed[:другое_поле]
Должно существовать соответствующее поле с именем {поле}_confirmation, если не указано другое.
// По умолчанию: сравнивает с `password_confirmation`
'password' => 'required|min:8|confirmed'// Свое поле: сравнивает с `pass_confirm`
'password' => 'required|min:8|confirmed:pass_confirm'Обычно используется для паролей, подтверждения email или любых полей, которые нужно ввести дважды.
same:поле
Поле должно иметь то же значение, что и указанное поле.
'password' => 'same:password_confirmation'different:поле
Поле должно иметь значение, отличное от указанного поля.
'username' => 'different:email'in:значение1,значение2,...
Значение поля должно быть одним из указанных.
'status' => 'in:active,inactive,pending'not_in:значение1,значение2,...
Значение поля не должно быть одним из указанных.
'role' => 'not_in:admin,root'json
Поле должно быть корректной JSON-строкой.
'meta' => 'json'lowercase
Поле должно быть строкой в нижнем регистре.
'slug' => 'lowercase'uppercase
Поле должно быть строкой в верхнем регистре.
'code' => 'uppercase'url[:протоколы]
Поле должно быть корректным URL. Можно указать допустимые протоколы (например, http, https, steam).
'website' => 'url' // Любой корректный URL
'game' => 'url:steam,minecraft' // Должен начинаться с указанных схемuuid[:версии]
Поле должно быть корректным UUID по RFC 9562. Можно указать допустимые версии UUID (1-8).
'id' => 'uuid' // Любая версия
'id' => 'uuid:4' // Только версия 4
'id' => 'uuid:1,4,6' // Несколько допустимых версийЧисла
numeric
Поле должно быть числом (целым или с плавающей точкой).
'price' => 'numeric'integer
Поле должно быть целым числом.
'quantity' => 'integer'decimal:мин,макс
Поле должно содержать указанное количество знаков после запятой.
'amount' => 'decimal:2,4' // Допустимо: 1.23, 5.678, 10.0000gt:поле
Поле должно быть больше указанного поля.
'price' => 'gt:discounted_price'gte:поле
Поле должно быть больше или равно указанному полю.
'end_date' => 'gte:start_date'lt:поле
Поле должно быть меньше указанного поля.
'discounted_price' => 'lt:price'lte:поле
Поле должно быть меньше или равно указанному полю.
'start_date' => 'lte:end_date'min_digits:значение
Поле должно быть числовой строкой с минимальным количеством цифр.
'code' => 'min_digits:6'max_digits:значение
Поле должно быть числовой строкой с максимальным количеством цифр.
'pin' => 'max_digits:4'Массивы
array
Поле должно быть массивом.
'tags' => 'array'contains:значение1,значение2,...
Массив должен содержать хотя бы одно из указанных значений.
'roles' => 'array|contains:admin,editor'
// Верно, если roles содержит 'admin' или 'editor'Если поле - строка, оно будет сравниваться как единое значение.
distinct
Массив не должен содержать повторяющихся значений.
'tags' => 'array|distinct'
// ['php', 'html', 'php'] → ❌
// ['php', 'html', 'css'] → ✅Работает с примитивными значениями (string, int).
in_array:другое_поле.*
Значение должно существовать в другом массиве.
'country' => 'in_array:countries.*'
/*
[
'country' => 'UA',
'countries' => ['UA', 'PL', 'DE']
]
*/Можно также сравнивать с простым массивом:
'item' => 'in_array:available_items'Даты
date
Поле должно быть корректной датой.
'birthdate' => 'date'date_equals:поле_или_дата
Дата должна точно совпадать с другой датой или полем.
'start' => 'date_equals:end'
'fixed' => 'date_equals:2024-01-01'date_format:формат,...
Дата должна соответствовать хотя бы одному из указанных форматов.
'start' => 'date_format:Y-m-d'
'custom' => 'date_format:d/m/Y,H:i'Можно указать несколько форматов через запятую.
before:поле_или_дата
Дата должна быть раньше указанной.
'start' => 'before:end'before_or_equal:поле_или_дата
Дата должна быть раньше или равна указанной.
'start' => 'before_or_equal:end'after:поле_или_дата
Дата должна быть позже указанной.
'end' => 'after:start'
'event_date' => 'after:2025-06-01'after_or_equal:поле_или_дата
Дата должна быть позже или равна указанной.
'end' => 'after_or_equal:start'Файлы
file
Поле должно быть загруженным файлом.
'avatar' => 'file'image
Поле должно быть изображением (jpeg, png, gif, webp и др.).
'avatar' => 'image'mimes:тип1,тип2,...
MIME-тип файла должен соответствовать одному из указанных.
'document' => 'file|mimes:application/pdf'
'photo' => 'image|mimes:image/jpeg,image/png'Проверяется по внутреннему MIME-типу (на основе заголовков).
extensions:расш1,расш2,...
Имя файла должно иметь одно из указанных расширений.
'upload' => 'file|extensions:pdf,docx'Менее строгая проверка, чем mimes - только по имени файла.
dimensions:мин_ширина=100,макс_высота=800,...
Проверяет размеры изображения.
'photo' => 'image|dimensions:min_width=200,max_height=500'Доступные параметры:
width,heightmin_width,max_widthmin_height,max_height
Базы данных
unique:таблица,столбец[,исключитьID|поле,исключитьЗначение]
Значение поля должно быть уникальным в указанной таблице и столбце.
'email' => 'unique:modUserProfile,email'Исключение текущей записи Можно исключить запись по ID:
'email' => 'unique:modUserProfile,email,123' // где id != 123Или по другому столбцу:
'slug' => 'unique:modResource,alias,createdby,3' // где createdby != 3exists:таблица,столбец[,исключитьID|поле,исключитьЗначение]
Значение поля должно существовать в указанной таблице и столбце.
'country' => 'exists:pbTableValue,code'Исключение записи из проверки Можно пропустить одну запись по ID или столбцу:
'email' => 'exists:modUserProfile,email,123' // где id != 123
'email' => 'exists:modUserProfile,email,blocked,1' // где blocked != 1Универсальные
Эти правила применяются к строкам, числам, массивам и файлам. Поведение зависит от типа ввода.
min:значение
Поле должно иметь минимальное:
- Число: не менее значения
- Строка: не менее символов
- Массив: не менее элементов
- Файл: не менее килобайт
'age' => 'min:18', // число ≥ 18
'title' => 'min:10', // длина ≥ 10 символов
'tags' => 'array|min:2', // ≥ 2 элементов
'avatar' => 'file|min:100', // ≥ 100 KBmax:значение
Поле не должно превышать:
- Число: максимальное значение
- Строка: максимальную длину
- Массив: максимальное количество элементов
- Файл: максимальный размер
'age' => 'max:99',
'title' => 'max:255',
'tags' => 'array|max:5',
'avatar' => 'file|max:500',between:мин,макс
Поле должно находиться в диапазоне:
- Число: значение в диапазоне
- Строка: длина в диапазоне
- Массив: количество элементов
- Файл: размер в KB
'age' => 'between:18,65',
'title' => 'between:10,100',
'tags' => 'array|between:2,4',
'avatar' => 'file|between:100,500',size:значение
Поле должно точно соответствовать:
- Число: равно значению
- Строка: точное количество символов
- Массив: точное количество элементов
- Файл: точный размер
'code' => 'size:6', // строка из 6 символов
'count' => 'size:10', // число == 10
'tags' => 'array|size:3', // ровно 3 элемента
'avatar' => 'file|size:300',// ровно 300 KBrequired
Поле обязательно для заполнения.
'name' => 'required'required_if:другое_поле,значение
Поле обязательно, если другое поле равно указанному значению.
'promo_code' => 'required_if:has_discount,true'nullable
Позволяет полю быть null или пустым. Пропускает другие правила, если null.
'bio' => 'nullable|string'empty
Поле должно быть пустым. Используется для явной проверки отсутствия значения.
'honeypot' => 'empty'filled
Поле должно присутствовать и не быть пустым, если оно есть во вводе.
'nickname' => 'filled'exclude
Поле будет исключено из проверенных данных.
'debug' => 'exclude'exclude_if:другое_поле,значение
Поле будет исключено, если другое поле имеет указанное значение.
'discount_reason' => 'exclude_if:has_discount,false'Исключенные поля удаляются из результата, даже если прошли проверку.