Laravel. Валидация данных. Часть третья из трех

21.09.2020

Теги: LaravelWeb-разработкаТеорияФормаФреймворкШаблонСайта

Создание валидатора вручную

Если по каким-то причинам нет желания использовать трейт ValidatesRequests и его метод validate(), можно создать экземпляр валидатора вручную с помощью фасада Validator, используя метод make(). Первый аргумент метода — данные, подлежащие проверке, второй аргумент — правила валидации, которые применяются к этим данным.

<?php
namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;

class PostController extends Controller {
    /* ... */
    public function store(Request $request) {
        $validator = Validator::make($request->all(), [
            'title' => 'required|unique:posts|min:3|max:100',
            'excerpt' => 'required|min:100|max:200',
            'body' => 'required',
        ]);
        // данные не прошли проверку, выполняем редирект
        if ($validator->fails()) {
            return redirect('post/create')
                ->withErrors($validator)
                ->withInput();
        }
        // данные прошли проверку, можно сохранять в БД
    }
    /* ... */
}

Если проверка не пройдена, можно использовать метод withErrors(), чтобы записать сообщения об ошибках в сессию. При использовании этого метода, переменная $errors будет автоматически передана в шаблон после редиректа, позволяя отобразить сообщения пользователю.

Хук после валидации

Валидатор позволяет использовать функции обратного вызова после завершения всех проверок. Это дает возможность выполнить еще несколько проверок и/или добавить еще несколько сообщений об ошибках.

<?php
namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class PostController extends Controller {
    /* ... */
    public function store(Request $request) {
        $validator = Validator::make($request->all(), [
            'title' => 'required|unique:posts|min:3|max:100',
            'excerpt' => 'required|min:100|max:200',
            'body' => 'required',
        ]);
        // доп.проверка и запись доп.сообщения об ошибке
        $validator->after(function ($validator) {
            if ($this->somethingInvalid()) {
                $validator->errors()->add('field', 'Это еще одно сообщение об ошибке');
            }
        });
        // данные не прошли проверку, выполняем редирект
        if ($validator->fails()) {
            return redirect('post/create')
                ->withErrors($validator)
                ->withInput();
        }
        // данные прошли проверку, можно сохранять в БД
    }
    /* ... */
}

Работа с сообщениями об ошибках

Вызов метода errors() в экземпляре валидатора возвращает экземпляр Illuminate\Support\MessageBag, который имеет ряд методов для работы с сообщениями об ошибках (подробнее здесь). Переменная $errors, которая автоматически становится доступной для всех шаблонов, также является экземпляром класса MessageBag.

$errors = $validator->errors();

Получить первое сообщения об ошибке для заданного поля:

echo $errors->first('email');

Получить все сообщения об ошибках для выбранного поля:

foreach ($errors->get('email') as $message) {
    echo $message, '<br>';
}

Получить сообщения, если поле формы является массивом:

foreach ($errors->get('attachments.*') as $message) {
    echo $message, '<br>';
}

Получить все сообщения об ошибках для всех полей:

foreach ($errors->all() as $message) {
    echo $message, '<br>';
}

Определить наличие сообщения для определенного поля:

if ($errors->has('email')) {
    foreach ($errors->get('email') as $message) {
        echo $message, '<br>';
    }
}

Текст сообщений об ошибках

Текст сообщений об ошибках задается в файлах, которые расположены в директории resources/lang/(en|ru). Директория ru изначально не существует, так что ее надо создать и скопировать в нее все из директории en. Сообщения валидации данных, как нетрудно догадаться, расположены в файле validation.php.

<?php
return [

    /*
    |--------------------------------------------------------------------------
    | Validation Language Lines
    |--------------------------------------------------------------------------
    |
    | The following language lines contain the default error messages used by
    | the validator class. Some of these rules have multiple versions such
    | as the size rules. Feel free to tweak each of these messages here.
    |
    */

    'accepted' => 'The :attribute must be accepted.',
    'active_url' => 'The :attribute is not a valid URL.',
    'after' => 'The :attribute must be a date after :date.',
    'after_or_equal' => 'The :attribute must be a date after or equal to :date.',
    'alpha' => 'The :attribute may only contain letters.',
    'alpha_dash' => 'The :attribute may only contain letters, numbers, dashes and underscores.',
    'alpha_num' => 'The :attribute may only contain letters and numbers.',
    'array' => 'The :attribute must be an array.',
    'before' => 'The :attribute must be a date before :date.',
    'before_or_equal' => 'The :attribute must be a date before or equal to :date.',
    'between' => [
        'numeric' => 'The :attribute must be between :min and :max.',
        'file' => 'The :attribute must be between :min and :max kilobytes.',
        'string' => 'The :attribute must be between :min and :max characters.',
        'array' => 'The :attribute must have between :min and :max items.',
    ],
    'boolean' => 'The :attribute field must be true or false.',
    'confirmed' => 'The :attribute confirmation does not match.',
    'date' => 'The :attribute is not a valid date.',
    'date_equals' => 'The :attribute must be a date equal to :date.',
    'date_format' => 'The :attribute does not match the format :format.',
    'different' => 'The :attribute and :other must be different.',
    'digits' => 'The :attribute must be :digits digits.',
    'digits_between' => 'The :attribute must be between :min and :max digits.',
    'dimensions' => 'The :attribute has invalid image dimensions.',
    'distinct' => 'The :attribute field has a duplicate value.',
    'email' => 'The :attribute must be a valid email address.',
    'ends_with' => 'The :attribute must end with one of the following: :values.',
    'exists' => 'The selected :attribute is invalid.',
    'file' => 'The :attribute must be a file.',
    'filled' => 'The :attribute field must have a value.',
    'gt' => [
        'numeric' => 'The :attribute must be greater than :value.',
        'file' => 'The :attribute must be greater than :value kilobytes.',
        'string' => 'The :attribute must be greater than :value characters.',
        'array' => 'The :attribute must have more than :value items.',
    ],
    'gte' => [
        'numeric' => 'The :attribute must be greater than or equal :value.',
        'file' => 'The :attribute must be greater than or equal :value kilobytes.',
        'string' => 'The :attribute must be greater than or equal :value characters.',
        'array' => 'The :attribute must have :value items or more.',
    ],
    'image' => 'The :attribute must be an image.',
    'in' => 'The selected :attribute is invalid.',
    'in_array' => 'The :attribute field does not exist in :other.',
    'integer' => 'The :attribute must be an integer.',
    'ip' => 'The :attribute must be a valid IP address.',
    'ipv4' => 'The :attribute must be a valid IPv4 address.',
    'ipv6' => 'The :attribute must be a valid IPv6 address.',
    'json' => 'The :attribute must be a valid JSON string.',
    'lt' => [
        'numeric' => 'The :attribute must be less than :value.',
        'file' => 'The :attribute must be less than :value kilobytes.',
        'string' => 'The :attribute must be less than :value characters.',
        'array' => 'The :attribute must have less than :value items.',
    ],
    'lte' => [
        'numeric' => 'The :attribute must be less than or equal :value.',
        'file' => 'The :attribute must be less than or equal :value kilobytes.',
        'string' => 'The :attribute must be less than or equal :value characters.',
        'array' => 'The :attribute must not have more than :value items.',
    ],
    'max' => [
        'numeric' => 'The :attribute may not be greater than :max.',
        'file' => 'The :attribute may not be greater than :max kilobytes.',
        'string' => 'The :attribute may not be greater than :max characters.',
        'array' => 'The :attribute may not have more than :max items.',
    ],
    'mimes' => 'The :attribute must be a file of type: :values.',
    'mimetypes' => 'The :attribute must be a file of type: :values.',
    'min' => [
        'numeric' => 'The :attribute must be at least :min.',
        'file' => 'The :attribute must be at least :min kilobytes.',
        'string' => 'The :attribute must be at least :min characters.',
        'array' => 'The :attribute must have at least :min items.',
    ],
    'not_in' => 'The selected :attribute is invalid.',
    'not_regex' => 'The :attribute format is invalid.',
    'numeric' => 'The :attribute must be a number.',
    'password' => 'The password is incorrect.',
    'present' => 'The :attribute field must be present.',
    'regex' => 'The :attribute format is invalid.',
    'required' => 'The :attribute field is required.',
    'required_if' => 'The :attribute field is required when :other is :value.',
    'required_unless' => 'The :attribute field is required unless :other is in :values.',
    'required_with' => 'The :attribute field is required when :values is present.',
    'required_with_all' => 'The :attribute field is required when :values are present.',
    'required_without' => 'The :attribute field is required when :values is not present.',
    'required_without_all' => 'The :attribute field is required when none of :values are present.',
    'same' => 'The :attribute and :other must match.',
    'size' => [
        'numeric' => 'The :attribute must be :size.',
        'file' => 'The :attribute must be :size kilobytes.',
        'string' => 'The :attribute must be :size characters.',
        'array' => 'The :attribute must contain :size items.',
    ],
    'starts_with' => 'The :attribute must start with one of the following: :values.',
    'string' => 'The :attribute must be a string.',
    'timezone' => 'The :attribute must be a valid zone.',
    'unique' => 'The :attribute has already been taken.',
    'uploaded' => 'The :attribute failed to upload.',
    'url' => 'The :attribute format is invalid.',
    'uuid' => 'The :attribute must be a valid UUID.',

    /*
    |--------------------------------------------------------------------------
    | Custom Validation Language Lines
    |--------------------------------------------------------------------------
    |
    | Here you may specify custom validation messages for attributes using the
    | convention "attribute.rule" to name the lines. This makes it quick to
    | specify a specific custom language line for a given attribute rule.
    |
    */

    'custom' => [
        'attribute-name' => [
            'rule-name' => 'custom-message',
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Custom Validation Attributes
    |--------------------------------------------------------------------------
    |
    | The following language lines are used to swap our attribute placeholder
    | with something more reader friendly such as "E-Mail Address" instead
    | of "email". This simply helps us make our message more expressive.
    |
    */

    'attributes' => [],

];

Нужно перевести на русский те сообщения об ошибках, которые используются на сайте. Если сообщение об ошибке по умолчанию не подходит для какого-то поля — можно задать свой вариант именно для этого поля:

<?php
return [
    /* ... */
    'custom' => [
        'title' => [
            'required' => 'Поле «:attribute» обязательно для заполнения',
            'unique' => 'Такое значение поля «:attribute» уже используется',
            'min' => 'Поле «:attribute» должно быть не меньше :min символов',
            'max' => 'Поле «:attribute» должно быть не больше :max символов',
        ],
        'excerpt' => [
            'required' => 'Поле «:attribute» обязательно для заполнения',
        ],
        'body' => [
            'required' => 'Поле «:attribute» обязательно для заполнения',
        ],
    ],
    /* ... */
];

Кроме того, нужно задать более понятные имена для полей — вместо title, excerpt и body:

<?php
return [
    /* ... */
    'attributes' => [
        'title' => 'Заголовок',
        'excerpt' => 'Анонс поста',
        'body' => 'Текст поста'
    ],
    /* ... */
];

Еще один способ задать текст сообщений об ошибках — передать их третьим аргументом методу make():

<?php
namespace App\Http\Controllers;

use App\Post;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Validator;

class PostController extends Controller {
    /* ... */
    public function store(Request $request) {
        $rules = [
            'title' => 'required|unique:posts|min:3|max:100',
            'excerpt' => 'required|min:100|max:200',
            'body' => 'required',
        ];
        $messages = [
            'required' => 'Поле «:attribute» обязательно для заполнения',
            'unique' => 'Такое значение поля «:attribute» уже используется',
            'min' => 'Поле «:attribute» должно быть не меньше :min символов',
            'max' => 'Поле «:attribute» должно быть не больше :max символов',
        ];
        $attributes = [
            'title' => 'Заголовок',
            'excerpt' => 'Анонс поста',
            'body' => 'Текст поста'
        ];

        $validator = Validator::make(
            $request->all(),
            $rules,
            $messages,
            $attributes
        );
        // данные не прошли проверку, выполняем редирект
        if ($validator->fails()) {
            return redirect('post/create')
                ->withErrors($validator)
                ->withInput();
        }
        // данные прошли проверку, можно сохранять в БД
    }
    /* ... */
}

Показ сообщений об ошибках

Мы уже знаем, что в шаблонах будет доступна переменная $errors, которая является экземпляром класса MessageBag.

<!-- resources/views/post/create.blade.php -->

<h1>Создать пост</h1>

@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
        </ul>
    </div>
@endif

Дополнительно

Поиск: Laravel • Web-разработка • Теория • Форма • Фреймворк • Шаблон сайта • Валидация

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.