Giới thiệu Validation trong Laravel:
Laravel cung cấp nhiều cách thức để validate dữ liệu. Thông thường nhất là dùng method validate của Request.
Validation Quickstart:
Xem xét ví dụ sau:
Route:
use App\Http\Controllers\PostController;
Route::get('/post/create', [PostController::class, 'create']);
Route::post('/post', [PostController::class, 'store']);Controller:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;
class PostController extends Controller
{
/**
* Show the form to create a new blog post.
*/
public function create(): View
{
return view('post.create');
}
/**
* Store a new blog post.
*/
public function store(Request $request): RedirectResponse
{
$validated = $request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
// The blog post is valid...
// Save post
return response()->noContent();
}
}Dừng ở lỗi validate đầu tiên:
Bạn có thể dừng validate ngay sau khi gặp lỗi đầu tiên bằng thuộc tính bail:
$request->validate([
'title' => 'bail|required|unique:posts|max:255',
'body' => 'required',
]);Trong ví dụ này, giả sử title gặp lỗi unique thì điều kiện max:255 sẽ không check nữa.
Field data lồng nhau: sử dụng dấu .
$request->validate([
'title' => 'required|unique:posts|max:255',
'author.name' => 'required',
'author.description' => 'required',
]);Hiển thị lỗi validate:
Nếu request field không pass được validate, Laravel sẽ tự động redirect về location trước. Lỗi validate và request input sẽ được flash vào session.
Biến $errors được share với tất cả view bởi middleware Illuminate\View\Middleware\ShareErrorsFromSession, cái mà được cung cấp bởi web middleware. Biến $errors là instance của Illuminate\Support\MessageBag. File create.blade.php như sau:
<!-- /resources/views/post/create.blade.php -->
<h1>Create Post</h1>
@if ($errors->any())
<div class="alert alert-danger">
<ul>
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<!-- Create Post Form -->Customize error messages:
Error message nằm trong file lang/en/validation.php. Sử dụng lệnh php artisan lang:publish để tạo thư mục lang ở ứng dụng, sau đó copy file sang thư mục ngôn ngữ khác (ví dụ vi) để thay đổi.
XHR Request:
Khi gửi XHR request từ javascript, sử dụng method validate sẽ không tạo ra 1 redirect response, thay vào đó Laravel khởi tạo 1 JSON response chứ tất cả validation error. JSON response này sẽ được gửi kèm cùng 422 HTTP status.
Chỉ thị @error:
<input id="title"
type="text"
name="title"
class="@error('title') is-invalid @enderror">
@error('title')
<div class="alert alert-danger">{{ $message }}</div>
@enderrorNếu bạn đang sử dụng named error bags, pass tên của error bag làm tham số thứ 2 của chỉ thị @error:
<input class="@error('title', 'post') is-invalid @enderror">Repopulating Forms:
Khi Laravel khởi tạo 1 redirect response khi gặp lỗi validatation, framework cũng tự động flash toàn bộ request input vào session. Bạn có thể hiển thị lại ở form. Để truy xuất flash input từ request trước, gọi method old:
$title = $request->old('title');Hoặc dùng global helper old. Trong blade:
<input type="text" name="title" value="{{ old('title') }}">Mặc định Laravel cung cấp TrimStrings và ConvertEmptyStringsToNull middleware trong Kernel, vì vậy bạn cần mark optional field là nullable nếu bạn không muốn validator coi giá trị null là không hợp lệ. Ví dụ:
$request->validate([
'title' => 'required|unique:posts|max:255',
'body' => 'required',
'publish_at' => 'nullable|date',
]);Chúng ta chỉ định field publish_at có thể null hoặc là 1 giá trị date. Nếu thuộc tính nullable không add vào rule, validator có thể coi null là giá trị không hợp lệ.
Validation error response format:
Khi app trả về exception Illuminate\Validation\ValidationException và dữ liệu HTTP đến là JSON, Laravel sẽ tự động format error message và trả về 422 Unprocessable Entity HTTP response.
Ví dụ về JSON response:
{
"message": "The team name must be a string. (and 4 more errors)",
"errors": {
"team_name": [
"The team name must be a string.",
"The team name must be at least 1 characters."
],
"authorization.role": [
"The selected authorization.role is invalid."
],
"users.0.email": [
"The users.0.email field is required."
],
"users.2.email": [
"The users.2.email must be a valid email address."
]
}
}Form request validation:
Tạo form request:
Form request là custom request class đóng gói validation và authorization logic:
php artisan make:request StorePostRequestClass sẽ được tạo ở app/Http/Requests. Form request có 2 method: authorize và rules. Method authorize xác thực user có thể thực hiện hành động không, rules trả về validation rule áp dụng cho request data:
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\Rule|array|string>
*/
public function rules(): array
{
return [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
];
}Bạn có thể type-hint bất kỳ phụ thuộc nào trong rules, nó sẽ được tự động resolve bởi service container.
Sau đó bạn cần type-hint request trong controller method. Request đến sẽ được validate trước khi controller method được gọi. Nghĩa là bạn không cần xử lý thêm ở controller:
/**
* Store a new blog post.
*/
public function store(StorePostRequest $request): Response
{
// The incoming request is valid...
// Retrieve the validated input data...
// Truy xuất toàn bộ dữ liệu đã được validate
$validated = $request->validated();
// Retrieve a portion of the validated input data...
// Truy xuất 1 phần dữ liệu
$validated = $request->safe()->only(['name', 'email']);
$validated = $request->safe()->except(['name', 'email']);
// Store the blog post...
return response()->noContent();
}Nếu validation fail, 1 redirect response sẽ được khởi tạo để gửi user quay lại location trước. Error được flash vào session và sẽ hiển thị ra. Nếu request là XHR, 1 HTTP response với status 422 sẽ được trả về kèm với JSON error.
Add after hook vào form request:
Nếu bạn muốn add 1 after hook vào form request, sử dụng method withValidator. Method này nhận 1 Validator, cho phép gọi bất kỳ method nào của nó:
use Illuminate\Validation\Validator;
/**
* Configure the validator instance.
*/
public function withValidator(Validator $validator): void
{
// Gọi sau khi validate
$validator->after(function (Validator $validator) {
// do it
});
}Dừng ở lỗi validate đầu tiên:
Thêm thuộc tính stopOnFirstFailure vào request form:
protected $stopOnFirstFailure = true;Custom redirect location:
protected $redirect = '/dashboard';
// hoặc dùng route:
protected $redirectRoute = 'dashboard';Authorizing Form request:
public function authorize(): bool
{
$comment = Comment::find($this->route('comment'));
return $comment && $this->user()->can('update', $comment);
}Bạn có thể type-hint bất kỳ phụ thuộc nào bạn cần trong method authorize. Nó sẽ tự động resolve bởi service container.
Customize error message:
Dùng method message:
public function messages(): array
{
return [
'title.required' => 'A title is required',
'body.required' => 'A message is required',
];
}Customize validation attributes:
Nhiều validation error chứa :attribute. Có thể custom cái này bằng method attributes:
public function attributes(): array
{
return [
'email' => 'email address',
];
}Chuẩn bị input cho validate:
Nếu bạn muốn chuẩn bị hoặc xử lý data trước khi áp dụng validate, sử dụng method prepareForValidation:
protected function prepareForValidation(): void
{
$this->merge([
'slug' => Str::slug($this->slug),
]);
}Tương tự, nếu bạn muốn xử lý request sau khi validate hoàn thành, sử dụng method passedValidation:
protected function passedValidation(): void
{
$this->replace(['name' => 'Taylor']);
}Tạo validators bằng tay:
Nếu bạn không muốn sử dụng validate method, bạn có thể tạo 1 instance Validator. Method make trong facade này khởi tạo 1 validator instance mới:
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Validator;
class PostController extends Controller
{
/**
* Store a new blog post.
*/
public function store(Request $request): Response
{
$validator = Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
]);
if ($validator->fails()) {
return redirect('post/create')
->withErrors($validator)
->withInput();
}
// Retrieve the validated input...
$validated = $validator->validated();
// Retrieve a portion of the validated input...
$validated = $validator->safe()->only(['name', 'email']);
$validated = $validator->safe()->except(['name', 'email']);
// Store the blog post...
return response()->noContent();
}
}Tham số thứ nhất của method make là data để validate. Tham số thứ 2 là mảng validation rule. Sau khi validate fail, method withErrors sẽ flash error message vào session. Khi sử dụng method này, biến $errors sẽ tự động được share cho view. Method withErrors nhận tham số là 1 validator, 1 MessageBag hoặc array.
Dừng ngay ở lỗi validate đầu tiên:
if ($validator->stopOnFirstFailure()->fails()) {
// ...
}Tự động redirect:
Nếu bạn vẫn muốn tạo instance validator bằng tay nhưng vẫn muốn tính năng auto redirect của method validate(), bạn có thể sử dụng validate() cho validator:
Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
])->validate();Có thể sử dụng method validateWithBag trong named error bag:
Validator::make($request->all(), [
'title' => 'required|unique:posts|max:255',
'body' => 'required',
])->validateWithBag('post');Named Error Bags:
Nếu bạn có nhiều form trong 1 page, bạn có thể cần đặt tên MessageBag chứa validate error, cho phép bạn truy xuất error message cho 1 form nhất định:
return redirect('register')->withErrors($validator, 'login');Bạn có thể tiếp cận tên MessageBag instance từ biến $errors:
{{ $errors->login->first('email') }}Customize error message:
$validator = Validator::make($input, $rules, $messages = [
'required' => 'The :attribute field is required.',
]);Ví dụ $messages:
$messages = [
'same' => 'The :attribute and :other must match.',
'size' => 'The :attribute must be exactly :size.',
'between' => 'The :attribute value :input is not between :min - :max.',
'in' => 'The :attribute must be one of the following types: :values',
];Chỉ định custom message cho 1 attribute:
Sử dụng cấu trúc tên thuộc tính . rule:
$messages = [
'email.required' => 'We need to know your email address!',
];Custom tên của attribute:
$validator = Validator::make($input, $rules, $messages, [
'email' => 'email address',
]);After validation hook:
Bạn có thể add callback sau khi validation complete, cho phép bạn thực hiện thêm tác vụ hoặc thêm error message:
use Illuminate\Support\Facades;
use Illuminate\Validation\Validator;
$validator = Facades\Validator::make(/* ... */);
$validator->after(function (Validator $validator) {
if ($this->somethingElseIsInvalid()) {
$validator->errors()->add(
'field', 'Something is wrong with this field!'
);
}
});
if ($validator->fails()) {
// ...
}Xử lý dữ liệu đã validate:
// sử dụng request:
$validated = $request->validated();
// nếu sử dụng validator:
$validated = $validator->validated();Bạn có thể sử dụng method safe(). Method này trả về instance của Illuminate\Support\ValidatedInput. Object này có method only, except, all để truy xuất 1 phần hoặc toàn bộ dữ liệu đã validate.
$validated = $request->safe()->only(['name', 'email']);
$validated = $request->safe()->except(['name', 'email']);
$validated = $request->safe()->all();Bạn cũng có thể loop hoặc truy xuất như 1 array:
// Validated data may be iterated...
foreach ($request->safe() as $key => $value) {
// ...
}
// Validated data may be accessed as an array...
$validated = $request->safe();
$email = $validated['email'];Nếu bạn muốn thêm field vào data, dùng method merge:
$validated = $request->safe()->merge(['name' => 'Taylor Otwell']);Truy xuất data dưới dạng collection:
$collection = $request->safe()->collect();Làm việc với error message:
Sau khi gọi errors method ở instance Validator, bạn sẽ nhận 1 MessageBag instance, cung cấp nhiều method để làm việc với error message. Biến $errors tự động có sẵn ở view chính là instance của MessageBag.
Truy xuất error message đầu tiên của 1 field:
$errors = $validator->errors();
echo $errors->first('email');Truy xuất toàn bộ error message của 1 field:
foreach ($errors->get('email') as $message) {
// ...
}Sử dụng . để truy xuất các message của array form field:
foreach ($errors->get('attachments.*') as $message) {
// ...
}Truy xuất toàn bộ message của toàn bộ field:
foreach ($errors->all() as $message) {
// ...
}Xác minh 1 message tồn tại cho 1 field:
if ($errors->has('email')) {
// ...
}