Формы и валидация

7 / 21
22 мин чтения
Введение
О чём говорим

В прошлой части мы разобрали, как данные превращаются в HTML: view/Blade в Laravel против страницы+компонента+шаблона в Битриксе.

Теперь логичный шаг — “обратное направление”: как UI принимает данные от пользователя и как вы не превращаете проект в набор if (empty($_POST['NAME'])).

Формы — это не просто <form>...</form>. Это одновременно:

  • входная точка безопасности (CSRF, XSS, загрузки файлов),
  • граница домена (какие данные мы вообще принимаем и в каком формате),
  • UX‑контракт (какие ошибки показываем, где и когда),
  • и часто — точка боли в legacy (валидация размазана по шаблонам/компонентам/страницам).

В этой части разберём:

  • как правильно строить поток “форма → обработка → ошибки → повторный ввод”,
  • как Laravel делает валидацию “как слой” через Form Request,
  • как это обычно решают в Битриксе через компоненты / контроллеры / Result+Errors,
  • и где лучше делать валидацию: на входе, в ORM, в событиях, или везде понемногу.

Создание форм

Одна и та же задача в обоих стеках

Схема почти везде одинаковая:

  1. Отдать HTML формы (GET).
  2. Принять данные (POST).
  3. Провалидировать.
  4. Если ошибки — вернуть форму с ошибками и “старыми” данными.
  5. Если всё ок — выполнить действие (создать запись/отправить письмо) и показать успех.

Разница в том, насколько стандартно это делается.

Laravel: Blade‑форма, CSRF, old input и ошибки

HTML‑форма (Blade)

Типовой пример формы “обратной связи”:

        <form method="POST" action="{{ route('feedback.store') }}" enctype="multipart/form-data">
    @csrf

    <label>
        Имя
        <input name="name" value="{{ old('name') }}">
        @error('name')
            <div class="error">{{ $message }}</div>
        @enderror
    </label>

    <label>
        Email
        <input name="email" value="{{ old('email') }}">
        @error('email')
            <div class="error">{{ $message }}</div>
        @enderror
    </label>

    <label>
        Сообщение
        <textarea name="message">{{ old('message') }}</textarea>
        @error('message')
            <div class="error">{{ $message }}</div>
        @enderror
    </label>

    <label>
        Файл (опционально)
        <input type="file" name="attachment">
        @error('attachment')
            <div class="error">{{ $message }}</div>
        @enderror
    </label>

    <button type="submit">Отправить</button>
</form>

    

Что здесь важно как “норма”:

  • @csrf — защита от CSRF. Это не “опция”, а обязательная привычка.
  • old('field') — возвращает введённые данные после ошибки валидации.
  • @error('field') — удобный вывод сообщений об ошибках (через error bag).

Практическая мысль: в Laravel форма — это часть стандартизированного потока, где “старые данные” и ошибки — не самодельная механика, а база.

Laravel: Form Request как контракт входных данных

В Laravel лучший “по‑умолчанию” путь — вынести валидацию в Form Request.

Идея: контроллер не должен знать “как проверять поля”, контроллер должен знать “что сделать с валидными данными”.

Пример:

        <?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;

final class StoreFeedbackRequest extends FormRequest
{
    public function authorize(): bool
    {
        return true;
    }

    public function rules(): array
    {
        return [
            'name' => ['required', 'string', 'max:100'],
            'email' => ['required', 'string', 'email:rfc,dns', 'max:255'],
            'message' => ['required', 'string', 'min:10', 'max:2000'],
            'attachment' => ['nullable', 'file', 'max:5120'],
            'source' => ['nullable', Rule::in(['site', 'landing', 'telegram'])],
        ];
    }

    public function messages(): array
    {
        return [
            'message.min' => 'Опишите задачу чуть подробнее (минимум :min символов).',
            'attachment.max' => 'Файл слишком большой (максимум :max КБ).',
        ];
    }

    public function attributes(): array
    {
        return [
            'name' => 'имя',
            'email' => 'email',
            'message' => 'сообщение',
            'attachment' => 'файл',
        ];
    }
}

    

А если нужен UX‑тюнинг — добавляйте messages() (свои тексты) и attributes() (человеческие названия полей). Плюс, в Form Request удобно держать проверку доступа через authorize().

Контроллер становится предсказуемым:

        <?php

namespace App\Http\Controllers;

use App\Http\Requests\StoreFeedbackRequest;
use Illuminate\Http\RedirectResponse;

final class FeedbackController extends Controller
{
    public function store(StoreFeedbackRequest $request): RedirectResponse
    {
        $data = $request->validated();

        // Здесь: сохранение / письмо / очередь — но НЕ проверка полей.

        return back()->with('status', 'Спасибо! Мы получили сообщение.');
    }
}

    

“Хуки” Form Request: нормализация и кросс‑валидация

Если вам нужно “подчистить” вход (trim/приведение типов) — используйте prepareForValidation(). Если нужно проверять правила “между полями” — withValidator()/after() (например, “или email, или телефон обязателен”).

Пример нормализации:

        protected function prepareForValidation(): void
{
    $this->merge([
        'email' => is_string($this->input('email')) ? trim($this->input('email')) : null,
    ]);
}

    

Пример кросс‑валидации:

        public function withValidator($validator): void
{
    $validator->after(function ($validator) {
        $source = (string)$this->input('source', '');
        $message = (string)$this->input('message', '');

        if ($source === 'landing' && mb_strlen(trim($message)) < 50) {
            $validator->errors()->add('message', 'Для заявки с лендинга опишите задачу чуть подробнее (минимум 50 символов).');
        }
    });
}

    
Важно Form Request — удобный контейнер для всего, что относится к входу: правила, тексты ошибок, человеческие имена полей, нормализация и (если нужно) проверка доступа.

Bitrix: форма через свой компонент (каноничный “сайтовый” путь)

Базовая идея компонента (без деталей проекта): принять POST, проверить сессию, собрать данные, провалидировать через Result, и отрендерить форму повторно с ошибками.

        <?php

use Bitrix\Main\Context;
use Bitrix\Main\Result;

final class FeedbackFormComponent extends CBitrixComponent
{
    public function executeComponent()
    {
        $request = Context::getCurrent()->getRequest();

        $this->arResult['VALUES'] = [
            'NAME' => (string)$request->getPost('NAME'),
            'EMAIL' => (string)$request->getPost('EMAIL'),
            'MESSAGE' => (string)$request->getPost('MESSAGE'),
        ];
        $this->arResult['ERRORS'] = [];
        $this->arResult['SUCCESS'] = false;

        if ($request->isPost() && check_bitrix_sessid()) {
            $result = $this->validate($this->arResult['VALUES']);

            if ($result->isSuccess()) {
                // TODO: сохранить/отправить
                $this->arResult['SUCCESS'] = true;
                $this->arResult['VALUES'] = ['NAME' => '', 'EMAIL' => '', 'MESSAGE' => ''];
            } else {
                $this->arResult['ERRORS'] = $result->getErrorMessages();
            }
        }

        $this->includeComponentTemplate();
    }

    private function validate(array $values): Result
    {
        $result = new Result();

        if (trim($values['NAME']) === '') {
            $result->addError(new \Bitrix\Main\Error('Укажите имя.'));
        }

        if (trim($values['EMAIL']) === '' || !check_email($values['EMAIL'])) {
            $result->addError(new \Bitrix\Main\Error('Укажите корректный email.'));
        }

        if (mb_strlen(trim($values['MESSAGE'])) < 10) {
            $result->addError(new \Bitrix\Main\Error('Сообщение слишком короткое.'));
        }

        return $result;
    }
}

    

В template.php обычно важно не забыть два “барьера”:

        <form method="POST" action="<?= POST_FORM_ACTION_URI ?>">
    <?= bitrix_sessid_post() ?>
    <!-- inputs + вывод ошибок -->
</form>

    

Если вы делаете формы на “сайте Битрикса”, компонент — хороший “контейнер” для полного потока: вход → проверка → результат → шаблон.

Bitrix: формы для AJAX/API через D7‑контроллеры и Action Filters

Если форма работает как AJAX (или вы делаете SPA/внешнюю интеграцию), зачастую чище вынести обработку в D7‑контроллер:

  • строгий JSON‑ответ,
  • единая точка prefilters (CSRF/метод/авторизация),
  • предсказуемая обработка ошибок.

Не так давно в BitrixFramework появился штатный механизм валидации на основе PHP‑атрибутов (DTO + правила), который отлично подходит именно для контроллеров и API.

Схематично (через автоподстановку валидируемого DTO):

        <?php

use Bitrix\Main\Engine\Controller;
use Bitrix\Main\Engine\ActionFilter;
use Bitrix\Main\Validation\Engine\AutoWire\ValidationParameter;
use Bitrix\Main\Validation\Rule\Email;
use Bitrix\Main\Validation\Rule\Length;
use Bitrix\Main\Validation\Rule\NotEmpty;

final class FeedbackDto
{
    public function __construct(
        #[NotEmpty]
        #[Length(max: 100)]
        public ?string $name = null,

        #[NotEmpty]
        #[Email]
        #[Length(max: 255)]
        public ?string $email = null,

        #[NotEmpty]
        #[Length(min: 10, max: 2000)]
        public ?string $message = null,
    ) {}

    public static function createFromRequest(\Bitrix\Main\HttpRequest $request): self
    {
        return new self(
            name: (string)$request->get('name'),
            email: (string)$request->get('email'),
            message: (string)$request->get('message'),
        );
    }
}

final class FeedbackController extends Controller
{
    public function getAutoWiredParameters()
    {
        return [
            new ValidationParameter(
                FeedbackDto::class,
                fn () => FeedbackDto::createFromRequest($this->getRequest()),
            ),
        ];
    }

    public function configureActions(): array
    {
        return [
            'send' => [
                'prefilters' => [
                    new ActionFilter\HttpMethod([ActionFilter\HttpMethod::METHOD_POST]),
                    new ActionFilter\Csrf(),
                ],
            ],
        ];
    }

    public function sendAction(FeedbackDto $dto): array
    {
        // TODO: сохранить/отправить
        return ['ok' => true];
    }
}

    

Если FeedbackDto невалиден, sendAction не выполнится — контроллер вернёт JSON‑ошибки автоматически (это как раз то, чего обычно не хватало для API‑стиля).

Валидация: один принцип для обоих стеков

Принцип, который спасает проекты

Валидация должна отвечать на два вопроса:

  1. Что мы принимаем? (формат, типы, длины, диапазоны)
  2. Имеем ли мы право это сделать? (контекст, авторизация, бизнес‑правила)

И второй пункт часто важнее: “валидные данные” не означают, что действие разрешено.

Как это обычно раскладывается по слоям:

  • Laravel: формат и ограничения — в rules(), “можно ли вообще выполнять действие” — в authorize() (и/или policy/gate), а контроллер работает уже с $request->validated().
  • Битрикс: проверка формата/обязательности — в одном месте (компонент/контроллер/валидатор), а права/контекст — через prefilters (в контроллерах) или явные проверки до выполнения действия (в компоненте/сервисе).

Laravel: Validation Rules, кастомные правила и сообщения

Базовые правила и “читабельная строгость”

Если у вас уже есть Form Request, этот блок — про то, чем обычно “докручивают” правила в реальных проектах:

  • bail — остановиться на первой ошибке правила (уменьшает “шум”).
  • Rule::in(...) — белый список значений (“type/status/source”).
  • Rule::unique(...) + ignore(...) — корректная уникальность на update.

Кастомное правило (валидатор “смыслом”, а не regex’ом)

Если логика сложнее, лучше сделать правило, а не писать 3 regex’а и 5 if.

        <?php

namespace App\Rules;

use Closure;
use Illuminate\Contracts\Validation\ValidationRule;

final class NoLinks implements ValidationRule
{
    public function validate(string $attribute, mixed $value, Closure $fail): void
    {
        $text = is_string($value) ? $value : '';

        if (preg_match('~https?://~i', $text) || str_contains(mb_strtolower($text), 'www.')) {
            $fail('Ссылки в сообщении запрещены.');
        }
    }
}

    

Использование:

        'message' => ['required', 'string', 'min:10', 'max:2000', new \App\Rules\NoLinks()],

    

Сообщения об ошибках и локализация

В Laravel, помимо точечных сообщений в Form Request, есть системный слой локализации — языковые файлы (lang/ru/validation.php), что удобно, когда вы хотите единый стиль ошибок по всему проекту.

Практическая мысль: тексты ошибок — часть UX. Если вы делаете продукт, это не “лишнее”, это то, что пользователь реально читает.

Bitrix: как не размазывать проверки — Result/ErrorCollection как контракт

В Bitrix форма легко “расползается” на проверки в компоненте, в шаблоне и ещё где‑то. Чтобы этого не произошло, удобнее зафиксировать простой контракт: валидация возвращает Result с ошибками, а UI‑слой только показывает их.

Это помогает держать правила в одном месте и переиспользовать их между разными входами (форма на сайте / AJAX / импорт).

Дальше у вас обычно два “здоровых” варианта (и их можно сочетать):

  • Компонентная форма (сайт): держите правила в отдельном валидаторе, который возвращает Result (как в примере выше с validate()), а компонент только прокидывает VALUES/ERRORS в шаблон.
  • AJAX/API (D7‑контроллеры): используйте DTO + PHP‑атрибуты и автопроверку параметров. Это даёт предсказуемые JSON‑ошибки без ручного “собирания” ответа.

Bitrix: валидация на уровне ORM (когда это оправдано)

В Bitrix D7 ORM у полей сущности есть валидаторы (например, длина или формат). Это полезно, когда:

  • данные могут попадать в таблицу не только из “формы на сайте” (например, импорт/интеграция),
  • и вы хотите дополнительный “предохранитель” на уровне слоя данных.

Упрощённая идея — валидаторы прямо в карте полей:

        use Bitrix\Main\ORM\Fields\StringField;
use Bitrix\Main\ORM\Fields\Validators\LengthValidator;
use Bitrix\Main\ORM\Fields\Validators\RegExpValidator;

(new StringField('EMAIL'))
    ->configureRequired(true)
    ->addValidator(new LengthValidator(3, 255))
    ->addValidator(new RegExpValidator('~^.+@.+\..+$~'));

    

Если ORM‑операция не проходит, вы обычно получите ошибки в AddResult/UpdateResult, и их можно читать через getErrorMessages() / getErrors() / getErrorCollection().

Практическая мысль: ORM‑валидация — это “нижний уровень защиты”. Пользовательские ошибки и UX‑тексты чаще всё равно лучше держать в “валидации на входе”, чтобы контролировать сообщение, контекст и поля формы.

Где чаще всего ломаются формы (в обоих стеках)

Кейс A. Валидация “после действия”

Классика: сначала сохранили, потом поняли, что email плохой.
Решение одинаковое: валидировать до записи, а лучше — на уровне входного слоя (request/контроллер/компонент).

Кейс B. Ошибки без привязки к полям

Пользователь получает “что-то не так”, но не понимает где.
В Laravel это решается ошибками на конкретные поля (@error('field')).
В Bitrix часто нужно договориться о структуре ошибок (например, код ошибки = имя поля) и рендерить их рядом с инпутом.

Кейс C. CSRF “забыли один раз — и всё”

  • Laravel: @csrf в формах.
  • Bitrix: bitrix_sessid_post() + check_bitrix_sessid() на обработке.

Кейс D. Загрузки файлов

Валидация файла — это не только “размер”. В нормальном проекте вы почти всегда хотите проверить:

  • тип/формат,
  • ограничения на публичный доступ,
  • и место хранения (чтобы “пользовательский файл” не оказался в зоне исполнения).

Опыт: любую загрузку файлов оборачивайте строгой валидацией, независимо от стека.

Сравнительная таблица: формы и валидация

Шкала (как и раньше): от -2 до +2.

Критерий Laravel (баллы) Битрикс (баллы) Комментарий
Готовый “поток формы” (old input + вывод ошибок) +2 0 В Laravel это базовая механика. В Битриксе чаще делается руками/соглашениями.
Валидация как отдельный слой (Form Request) +2 +2 В BitrixFramework теперь есть штатная валидация DTO через атрибуты и ValidationService/ValidationParameter, что заметно приближает опыт к “request validation” в Laravel.
CSRF и базовая безопасность формы +2 +2 В обоих стеках есть понятная механика, главное — дисциплина.
Кастомные правила и читаемость валидации +2 +1 В Laravel это очень “нативно” (Rules + сообщения + локализация). В Bitrix обычно либо ручные проверки, либо собственный валидаторный слой.
Валидация на уровне данных (ORM) +1 +2 В Bitrix у ORM валидаторы — часть карты сущности. В Laravel чаще делают валидацию на входе + ограничения БД (и это нормальная стратегия).
Единый стиль ошибок для UI и API +1 +1 Laravel автоматически отдаёт JSON‑ошибки (и статус 422), если запрос “хочет JSON”. В BitrixFramework для контроллеров есть штатный JSON‑ответ с ошибками при автопроверке DTO (а вот для компонентных форм UI‑отображение ошибок всё равно остаётся задачей проекта).
Итого за статью +10 +8
Общий счёт (накопительный) +57 +30 Счёт накапливается по мере выхода статей.
Заключение
Laravel делает валидацию стандартом, Bitrix — задачей команды

Laravel хорош тем, что “правильный путь” встроен в каркас: Form Request, единый вывод ошибок, old input, понятный поток UI → validation → action.

Битрикс хорош тем, что вы можете построить очень практичный поток через компоненты (для сайта) и D7‑контроллеры (для AJAX/API), и собирать ошибки через Result/ErrorCollection. Но чтобы формы не деградировали, вам почти всегда нужны соглашения:

  • где живёт валидация,
  • как формируются ошибки,
  • как рендерятся ошибки и сохраняются значения,
  • и как вы не размазываете проверки по template.php.

В следующей части перейдём к теме, где валидация и безопасность выходят на первый план: аутентификация и авторизация (Breeze/Jetstream/Fortify в Laravel vs пользователи и права в Битриксе).

🛠 Практическая работа

одна форма, два подхода, одинаковый UX

Цель: сделать одну и ту же форму “обратной связи” так, чтобы:

  • пользователь видит ошибки у конкретных полей,
  • введённые значения сохраняются при ошибке,
  • есть CSRF,
  • есть кастомное правило (“нельзя вставлять ссылки”),
  • и один и тот же код валидации можно использовать повторно.

Задание для Laravel

  1. Сделайте страницу /feedback с формой (Blade).
  2. Добавьте POST /feedback и контроллер FeedbackController@store.
  3. Создайте StoreFeedbackRequest с правилами и кастомными сообщениями.
  4. Добавьте кастомное правило NoLinks для поля message.
  5. При успехе — редирект назад с flash‑сообщением “успех”, при ошибке — показать ошибки у полей и вернуть old input.

Опорные доки: Validation, Requests (Form Requests).

Задание для Битрикса

Вариант A (сайтовый, компонентный):

  1. Создайте свой компонент vendor:feedback.form.
  2. В компоненте реализуйте поток POST‑обработки:
    • check_bitrix_sessid() как обязательный барьер,
    • сбор входных данных,
    • валидация через отдельный метод/класс, возвращающий Result,
    • ошибки → $arResult['ERRORS'], значения → $arResult['VALUES'].
  3. В template.php:
    • bitrix_sessid_post(),
    • вывод ошибок рядом с полями (минимум — списком сверху),
    • экранирование через htmlspecialcharsbx.

Вариант B (AJAX/API‑стайл):

  1. Сделайте D7‑контроллер с sendAction.
  2. Подключите action filters (POST + CSRF).
  3. Верните JSON со структурой ok: true/false и сообщениями ошибок (можно через addErrors).
  4. На фронте (даже без фреймворка) отрисуйте ошибки у полей.

Цель упражнения — почувствовать: в Bitrix качество форм чаще зависит не от “фичи ядра”, а от того, внедрили ли вы стандарт обработки и ошибок в проекте.

Мы используем файлы cookie для улучшения работы сайта. Продолжая использовать сайт, вы соглашаетесь с нашей политикой конфиденциальности.

AI Домовой История

0 / 100

Привет! Я помогу с вопросами по 1С-Битрикс.

Спрашивай про D7, ORM, компоненты или события.

Требуется авторизация

Войдите или зарегистрируйтесь, чтобы задавать вопросы AI-ассистенту.

Войти