Validation trong Laravel (Phần 3)
NỘI DUNG BÀI VIẾT
Mở đầu
Trong hai bài viết trước của serie, mình đã giới thiệu với các bạn về vấn đề kiểm tra tính đúng đắn của dữ liệu nhập vào do người dùng cung cấp, cụ thể là cách kiểm trả dữ liệu và hiển thị lại lỗi cho người dùng. Ở bài viết này, mình sẽ hướng dẫn các bạn cách tạo ra những điều kiện dùng để kiểm tra dữ liệu do chính các bạn định nghĩa.
Vấn đề
Laravel mặc định đã cung cấp cho chúng ta rất nhiều các điều kiện có thể sử dụng để kiểm tra dữ liệu đến.

Tuy nhiên trong thực tế, không phải chỉ nhữngđiều kiện này là đủ cho project của chúng ta mà chúng ta có những điều kiện riêng cho project. Laravel cũng đã tính toán tới vấn đề này vì thế nó đã cung cấp cho chúng ta một số cách để tạo ra các điều kiện mới theo yêu cầu của cá nhân.
Custom validation rules
Giả sử chúng ta có 2 field cần kiểm trả lần lượt là:
<input type='text' name='name'>
<input type='text' name='number'>
Code language: HTML, XML (xml)
Và chúng ta muốn validation 2 field trên với điều kiện như sau:
name
:- Không được để trống
- Tối thiểu 6 kí tự
- Tất cả các từ phải viết hoa
number
:- Không được để trống
- Là số
- Tổng của các chữ cái trong
name
+number
phải là số chẵn
Đây là form của chúng ta:

Chúng ta sẽ sử dụng FormRequest như trong bài viết trước đấy để validate form trên. Còn đây là những điều kiện cơ bản của Laravel mà ta dùng để validate 2 field trên:
public function rules()
{
return [
'name' => 'required|size:6',
'number' => 'required|numeric',
];
}
Code language: PHP (php)
Với nội dung điều kiện như trên, ta đã có thể thỏa mãn 2 điều điện đầu tiên của field name
và điều kiện thứ nhất của filed number
Để thực hiện việc validate tất cả các chữ viết hoa hay tổng các chứ của name
và number
chia hết cho 5, ta cần phải tự định nghĩa điều kiện này. Dưới đây là một số phương pháp mà Laravel cung cấp cho việc tự định nghĩa điều kiện:
1. Sử dụng Closures
Nếu học PHP
chắc hẳn bạn đã biết đến khái niệm Closure
, để định nghĩa một điều kiện sử dụng Closure, ta cần sửa lại phần điều kiện của các field thành một array
chứ không phải một string
như ở trên, việc sửa đổi như sau:
public function rules()
{
return [
'name' => [
'required',
'size:6',
],
'number' => [
'required',
'numeric'
]
];
}
Code language: PHP (php)
Tiếp đến ta thêm một Closure
có dạng như sau vào cả 2 field:
function ($attribute, $value, $fail) {
}
Code language: PHP (php)
Closure
trên gồm có 3 biến mặc định là:
$attribute
: chính là tên của field cần validate tương ứng, ở đây sẽ lần lượt làname
vànumber
.$value
: là giá trị nhận vào khi người dùng submit form.$fail
: là mộtcallback
được gọi đến khi việc validate thất bại. Đây chính là nơi mà bạn có thể truyền vào thông báo khi việc validate thất bại.
Sau khi thêm Closure
vào điều kiệncủa chúng ta sẽ có dạng như sau:
public function rules()
{
return [
'name' => [
'required',
'size:6',
function ($attribute, $value, $fail) {
}
],
'number' => [
'required',
'numeric'
function ($attribute, $value, $fail) {
}
]
];
}
Code language: PHP (php)
Đầu tiên chúng ta sẽ tiến hành định nghĩa điều kiện yêu cầu cho field name
phải được nhập vào dưới dạng uppercase như sau:
function ($attribute, $value, $fail) {
if (strtoupper($value) !== $value) {
return $fail("The $attribute must be upper case");
}
}
Code language: PHP (php)
Như bạn thấy ở trên, ta chỉ việc sử dụng hàm strtoupper()
có sẵn trong PHP để chuyển $value
người dùng nhập vào sang dạng viết hoa và so sánh nó với $value
gốc. Trường hợp $value
của người dùng nhập vào là viết hoa thì việc validate là thành công và không có lỗi gì.
Còn trong trường hợp ngườ dùng nhập không phải chữ in hoa sẽ lập tức dẫn đến điều kiện if
đúng và sẽ chạy hàm $fail()
và trả lại lỗi. Ở đây trong hàm $fail()
ta có thể dùng $attribute
để tạo thông báo về lỗi cho người dùng. Sau đó ta chạy thử submit lại form với điều kiện vừa nhập sẽ thu được kết quả như sau:
Trường hợp để form trống:

Trường hợp nhập nội dung cả 2 field như name
< 6 kí tự và không viết hoa:

- Nhập
name
> 6 kí tự nhưng không viết hoa:! - Nhập
name
thỏa mãn các điều kiện:

Như vậy ta đã tạo được điều kiện thứ nhất yêu cầu toàn bộ nội dung field name
nhập vào phải là uppercase. Tiếp đến với field number
ta không những cần $value
của chính nó mà còn cần cả $value
của field name
. Để thực hiện điều đó, ta làm như sau:
function ($attribute, $value, $fail) {
if ((strlen($this->name) + $value) % 2 != 0) {
return $fail("Sum of $attribute and name's total chars must be an even number");
}
}
Code language: PHP (php)
Mặc định trong FormRequest ta có thể truy cập đến giá trị của các field khác thông qua từ khóa $this->[name_of_the_field]
. Chính vì thể ở Closure
trên ta có thể dùng $this->name
để lấy giá trị field name
rồi dùng để tính tổng và kiểm tra xem có phải số chăn không. Ta có thể thử nghiệm lại điều kiện vừa tạo như sau:
- Để trống
name
và nhậpnumber
là số lẻ:

Như ta thấy 3 là số lẻ và name
để trống nên tổng là 3 và là số lẻ nên validate fail.
- Để trống
name
và nhậpnumber
là số chẵn:

Ở đây tổng là 4 là số chẵn nên validate thành công.
- Cuối cùng ta thử nhập cả
name
vànumber
sao cho tổng thỏa mãn:

Như vậy với name
gồm 6 ký tự và number
bằng 4 ta thu được tổng 10 là số chẵn nên thỏa mãn điều kiện của điều kiện ta vừa tạo. Trường hợp nhập tổng name
và number
lẻ sẽ xuất hiện lỗi:

Đây là kết quả cuối cùng chúng ta thu được trong hàm rules()
của FormRequest:
public function rules()
{
return [
'name' => [
'required',
'min:6',
function ($attribute, $value, $fail) {
if (strtoupper($value) !== $value) {
return $fail("The $attribute must be upper case");
}
},
],
'number' => [
'required',
'numeric',
function ($attribute, $value, $fail) {
if ((strlen($this->name) + $value) % 2 != 0) {
return $fail("Sum of $attribute and name's total chars must be an even number");
}
}
]
];
}
Code language: PHP (php)
Với cách sử dụng Closure
như ở trên, bạn đã có thể tự tạo ra những rule riêng phù hợp với project của mình tuy nhiên, trong trường hợp cùng một rule bạn tạo với Closure
nhưng được sử dụng ở nhiều nơi khác nhau thì cách làm trên có vẻ không ổn cho lắm. Nếu chẳng may, bạn có thay đổi rule sẽ phải đi đến từng vị trí bạn copy Closure
như ở ví dụ trên để sửa rất mất công. Chính vì thế, Laravel còn cung cấp cho chúng ta cách làm khác để giải quyết vấn đề này là Rule Object
. Tuy nhiên Rule Object
,
2. Rule Object
Rule Object
là một class mà trong đó bạn có thể định nghĩa các custom rule của bạn tương tự như Closure
và có thể gọi đến instance của class rule đó ở bất cứ đâu bạn cần. Nếu có thay đổi, bạn chỉ cần mở lại class đó lên và chỉnh sửa thì tất cả các vị trí sử dụng instance của class sẽ được cập nhật theo. Để tạo một Rule Object
, ta sử dụng cú pháp sau:
$ php artisan make:rule [class_name]
Ở đây mình sẽ làm ví dụ về cách tạo Rule Object
cho rule của field number
. Còn lại field name
bạn hãy dựa vào ví dụ của mình và tự làm theo để nắm rõ hơn cách sử dụng. Ta tiến hành tạo Rule Object
với cú pháp nói trên:
$ php artisan make:rule CustomEvenNumber
Xong khi gõ lệnh trên, trong folder app của bạn sẽ xuất hiện một thư mục mới là Rules
, bên trong đó sẽ chứa các class mà bạn dùng để định nghĩa các rule:

Đây là nội dung bên trong của class CustomEvenNumber
:
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CustomEvenNumber implements Rule
{
public function __construct()
{
//
}
public function passes($attribute, $value)
{
//
}
public function message()
{
return 'The validation error message.';
}
}
Code language: HTML, XML (xml)
Class này gồm có 3 hàm hình:
__construct()
: hàm khởi tạo của rule, nơi ta có thể truyền thêm các biến khác vàopasses()
: hàm để định nghĩa giá trị của filed cần valite có thỏa mãn hay không (2 biến$attribute
và$value
có giá trị tương tự như sử dụngClosure
)mesage()
: thông báo trả về nếu điều kiện không thỏa mãn
Vì điều kiện ta cần tạo yêu cầu giá trị của field name
, nên ta sẽ tiến hành truyền giá trị đó vào thông qua hàm __construct()
như sau:
protected $name;
public function __construct($name)
{
$this->name = $name;
}
Code language: PHP (php)
Tiếp đó trong hàm passes()
ta sẽ định nghĩa nội dung tương tự với cách làm Closure
nói trên. Tuy nhiên bạn nên chú ý rằng với cách sử dụng Closure
ta sẽ định nghĩa điều kiện dẫn đến việc validate thất bại còn trong hàm passes()
ta định nghĩa điều kiện validate thành công -> nội dung của Closure
và Rule Object
là ngược nhau. Vì thế nội dung hàm passes()
như sau:
public function passes($attribute, $value)
{
return (strlen($this->name) + $value) % 2 == 0;
}
Code language: PHP (php)
Còn nội dung của Closure
:
function ($attribute, $value, $fail) {
if ((strlen($this->name) + $value) % 2 != 0) {
return $fail("Sum of $attribute and name's total chars must be an even number");
}
}
Code language: PHP (php)
Bạn có thể thấy 2 điều kiện khác nhau, một là khác 0 còn một là bằng 0. Cuối cùng, trong hàm message()
ta copy lại nội dung từ bên Closure
và sửa lại một chút như sau:
public function message()
{
return "Sum of :attribute and name's total chars must be an even number";
}
Code language: PHP (php)
Đây là nội dung hoàn chỉnh của class CustomEventNumber
:
<?php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CustomEvenNumber implements Rule
{
protected $name;
public function __construct($name)
{
$this->name = $name;
}
public function passes($attribute, $value)
{
return (strlen($this->name) + $value) % 2 == 0;
}
public function message()
{
return "Sum of :attribute and name's total chars must be an even number";
}
}
Code language: HTML, XML (xml)
Quay lại bên FormReques ta sẽ xóa phần Closure
của field number
và thay class CustomEvenNumber
như sau:
'number' => [
'required',
'numeric',
new CustomEvenNumber($this->name),
]
Code language: PHP (php)
Tương tự như trong Closure
ta cũng phải truyền vào $this->name
là giá trị của field name
. Bạn nhớ use
class vừa tạo ở đầu class FormRequest:
use App\Rules\CustomEvenNumber;
Code language: PHP (php)
Cuối cùng, đây là nội dung hoàn chỉnh của FormRequest:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use App\Rules\CustomEvenNumber;
class CustomRequest extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => [
'required',
'min:6',
function ($attribute, $value, $fail) {
if (strtoupper($value) !== $value) {
return $fail("The $attribute must be upper case");
}
},
],
'number' => [
'required',
'numeric',
new CustomEvenNumber($this->name),
]
];
}
}
Code language: HTML, XML (xml)
Lưu ý: Khi dùng Rule Object
hay Closure
bạn đều có thể sử dụng Eloquent
để truy cập tới CSDL.
Kết bài
Đây là bài viết thứ 3 cũng là bài viết cuối cùng của mình về việc validate dữ liệu trong Laravel. Cảm ơn các bạn đã đọc.
Các bạn có thể tham khảo các bài viết hay về Laravel tại đây.
Hãy tham gia nhóm Học lập trình để thảo luận thêm về các vấn đề cùng quan tâm.
Tham khảo: Viblo
Leave a Reply