+2

Form và Validation trong Laravel

1. Form

1.1 Form là gì?

  • Form là một phần của giao diện người dùng được sử dụng để thu thập thông tin từ người dùng. Mục đích chính của form là để cho phép người dùng nhập và gửi thông tin, dữ liệu cho máy chủ.
  • Form thường được sử dụng trong các ứng dụng web để tạo các biểu mẫu đăng ký, đăng nhập, tìm kiếm, đặt hàng, đăng bài và nhiều tác vụ khác. Nó cung cấp một phương tiện để giao tiếp giữa người dùng và ứng dụng web.
  • Một form có thể chứa nhiều loại input, chẳng hạn như các ô nhập liệu, nút lệnh, radio button, checkbox và các thành phần khác để giúp người dùng cung cấp thông tin một cách dễ dàng và tiện lợi. Thông thường, dữ liệu được nhập vào các input này sẽ được gửi đến máy chủ thông qua phương thức POST hoặc GET. Ngoài ra còn có các phương thức khác như: PUT, DELETE, PATCH, HEAD.

1.2 Các loại form

Có nhiều loại form khác nhau tùy thuộc vào mục đích sử dụng của chúng và các thành phần cần thiết để thu thập thông tin từ người dùng. Dưới đây là một số loại form phổ biến:

  • Form đăng ký: Sử dụng để thu thập thông tin cá nhân của người dùng khi họ đăng ký tài khoản. Các trường thông tin thường bao gồm tên, email, mật khẩu, số điện thoại, địa chỉ,...
  • Form đăng nhập: Sử dụng để thu thập thông tin đăng nhập của người dùng như tên đăng nhập và mật khẩu để truy cập vào tài khoản của họ.
  • Form tìm kiếm: Cho phép người dùng tìm kiếm thông tin trên trang web bằng cách nhập từ khóa vào ô tìm kiếm.
  • Form liên hệ: Sử dụng để thu thập thông tin liên hệ từ người dùng như tên, email, số điện thoại và lời nhắn.
  • Form đặt hàng: Sử dụng để thu thập thông tin đơn hàng từ khách hàng bao gồm tên sản phẩm, số lượng, địa chỉ giao hàng và thông tin thanh toán.
  • Form đánh giá: Cho phép người dùng đánh giá sản phẩm hoặc dịch vụ của bạn bằng cách đánh giá từ một đến năm sao.
  • Form khảo sát: Sử dụng để thu thập ý kiến từ người dùng về các chủ đề cụ thể.
  • Form đăng bài: Cho phép người dùng đăng bài lên trang web hoặc diễn đàn, bao gồm các trường thông tin như tiêu đề, nội dung và hình ảnh.

Trên thực tế, có rất nhiều loại form khác nhau, phù hợp với các mục đích sử dụng khác nhau.

1.3 Cách bảo mật form trong Laravel

Để bảo mật form trong Laravel, bạn có thể sử dụng một số cách như sau:

  • Sử dụng CSRF token: CSRF là viết tắt của Cross-Site Request Forgery, đây là một phương thức tấn công mà kẻ tấn công có thể giả mạo yêu cầu từ người dùng đến ứng dụng web. Laravel cung cấp CSRF token để giải quyết vấn đề này. Bạn có thể sử dụng directive @csrf để thêm CSRF token vào form của mình.
<form method="POST" action="/example">
    @csrf
    // ...
</form>
  • Sử dụng HTTPS: HTTPS là một giao thức an toàn và mã hóa dữ liệu trên mạng. Sử dụng HTTPS sẽ giúp ngăn chặn những kẻ tấn công giả mạo yêu cầu của người dùng.
  • Sử dụng validation: Laravel cung cấp các rule validation để kiểm tra dữ liệu được gửi đến từ người dùng. Bạn nên kiểm tra dữ liệu đầu vào của mình để đảm bảo rằng chúng không bị tấn công.

Trên đây là một số cách để bảo mật form trong Laravel. Tuy nhiên, đây chỉ là một phần của việc bảo mật ứng dụng web của bạn. Bạn nên tìm hiểu thêm về bảo mật web để đảm bảo rằng ứng dụng của bạn là an toàn và không bị tấn công.

2. Validation

2.1 Validation để làm gì?

Validation được sử dụng để kiểm tra tính hợp lệ của dữ liệu được nhập vào từ người dùng trước khi xử lí dữ liệu hoặc lưu vào cơ sở dữ liệu. Nó giúp đảm bảo rằng dữ liệu được nhập vào đáp ứng các yêu cầu và ràng buộc được xác định trước, giúp tránh được các lỗi dữ liệu không mong muốn và tăng tính bảo mật cho ứng dụng.

2.2 Một số rule validation phổ biến

Laravel cung cấp nhiều rule validation cho việc kiểm tra dữ liệu đầu vào từ người dùng. Dưới đây là một số rule validation phổ biến:

  • required : Kiểm tra xem trường đầu vào có được cung cấp hay không.
  • email : Kiểm tra xem trường đầu vào có phải là địa chỉ email hợp lệ hay không.
  • numeric : Kiểm tra xem trường đầu vào có phải là giá trị số hay không.
  • integer : Kiểm tra xem trường đầu vào có phải là số nguyên hay không.
  • string : Kiểm tra xem trường đầu vào có phải là một chuỗi hay không.
  • min : Kiểm tra xem trường đầu vào có độ dài tối thiểu là bao nhiêu ký tự.
  • max : Kiểm tra xem trường đầu vào có độ dài tối đa là bao nhiêu ký tự.
  • unique : Kiểm tra xem trường đầu vào có giá trị đã tồn tại trong cơ sở dữ liệu hay không.
  • exists : Kiểm tra xem trường đầu vào có tồn tại trong bảng cơ sở dữ liệu nào đó hay không.
  • date : Kiểm tra xem trường đầu vào có phải là một ngày hợp lệ hay không.
  • before : Kiểm tra xem ngày trong trường đầu vào có trước ngày đã chỉ định hay không.
  • after : Kiểm tra xem ngày trong trường đầu vào có sau ngày đã chỉ định hay không.
  • confirmed : Kiểm tra xem giá trị trong trường đầu vào có khớp với giá trị xác nhận hay không.
  • in : Kiểm tra xem giá trị trong trường đầu vào có thuộc danh sách giá trị đã chỉ định hay không.

Dưới đây là một ví dụ đơn giản về validation trong một controller của Laravel:

<?php

namespace App\Http\Controllers;

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

class UserController extends Controller
{
    public function store(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'name' => 'required|max:255',
            'email' => 'required|email|unique:users,email|max:255',
            'password' => 'required|min:6|max:255',
        ]);

        if ($validator->fails()) {
            return redirect('register')
                ->withErrors($validator)
                ->withInput();
        }

        // Nếu dữ liệu hợp lệ, lưu vào cơ sở dữ liệu
        User::create([
            'name' => $request->input('name'),
            'email' => $request->input('email'),
            'password' => Hash::make($request->input('password')),
        ]);

        // Chuyển hướng về trang đăng nhập
        return redirect('login');
    }
}
  • Trong ví dụ này, chúng ta định nghĩa một hàm store() để xử lý request đăng ký người dùng. Đầu tiên, chúng ta sử dụng hàm Validator::make() để tạo một đối tượng validator và truyền vào dữ liệu đăng ký được gửi từ người dùng và các quy tắc validation.

  • Trong trường hợp này, chúng ta đang kiểm tra xem liệu trường "name" có bắt buộc nhập, không được quá 255 ký tự, trường "email" có đúng định dạng email, không được trùng lặp trong cơ sở dữ liệu, và không được quá 255 ký tự, và trường "password" có bắt buộc nhập, không được ngắn hơn 6 ký tự, và không được quá 255 ký tự.

  • Nếu validator phát hiện ra rằng dữ liệu không hợp lệ, nó sẽ trả về một đối tượng $validator với các thông báo lỗi cụ thể. Chúng ta sử dụng phương thức withErrors() để chuyển đối tượng validator này vào view đăng ký để người dùng có thể biết được lỗi của mình ở đâu và có thể sửa lại dữ liệu. Chúng ta cũng sử dụng phương thức withInput() để giữ lại các giá trị đã nhập của người dùng trên form.

  • Nếu dữ liệu hợp lệ, chúng ta lưu dữ liệu người dùng vào cơ sở dữ liệu và chuyển hướng người dùng đến trang đăng nhập.

Nhìn ở trên chúng ta thấy rằng có vẻ thằng Controller này đang phải đảm đương hơi nhiều công việc, với việc xử lý logic đơn giản chúng ta đã phải tốn kha khá dòng code rồi, nếu logic phức tạp với nhiều validate hơn thì code sẽ thực sự rối. Đây là lúc ta cần đến Form Request.

2.3 Form Request

Chúng ta có thể tạo FormRequest qua lệnh artisan:

php artisan make:request StoreBlogPostRequest

Lệnh này sẽ tạo ra cho chúng ta file StoreBlogPostRequest ở App\Http\Requests. File này gồm 2 default method là authorize() và rules().

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreBlogPostRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => 'required|max:255',
            'body' => 'required',
            'email' => [
                'required',
                'email',
                Rule::unique('users')->ignore($this->user),
            ],
        ];
    }
}

Trong ví dụ này, chúng ta đã tạo ra một Form Request StoreBlogPostRequest với hai trường title và body. Phương thức rules() xác định các quy tắc validation áp dụng cho các trường này.

Tuy nhiên, trong một số trường hợp, chúng ta có thể muốn thêm các rule hoặc kiểm tra dữ liệu bổ sung sau khi validate. Ví dụ, kiểm tra xem một email đã tồn tại trong database hay chưa, hoặc thêm một số điều kiện phức tạp để kiểm tra tính hợp lệ của dữ liệu.Để làm điều này, chúng ta có thể sử dụng phương thức withValidator(). Phương thức này nhận vào một tham số là đối tượng Validator và cho phép chúng ta thêm các rule hoặc kiểm tra dữ liệu bổ sung.

namespace App\Http\Requests;

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

class StoreBlogPostRequest extends FormRequest
{
    public function rules()
    {
        return [
            'name' => 'required|string',
            'email' => [
                'required',
                'email',
                Rule::unique('users')->ignore($this->user),
            ],
            'password' => 'required|string|min:6',
        ];
    }

    public function withValidator($validator)
    {
        $validator->after(function ($validator) {
            if ($validator->errors()->isEmpty()) {
                $email = $this->input('email');
                if ($this->user->email !== $email && User::where('email', $email)->exists()) {
                    $validator->errors()->add('email', 'Email already exists.');
                }
            }
        });
    }
}

Ở đây, chúng ta đã sử dụng phương thức after() của validator để thêm một closure để kiểm tra email đã tồn tại trong database sau khi dữ liệu được validate. Nếu có lỗi, chúng ta sẽ thêm lỗi vào validator bằng phương thức add() của validator. Nếu không có lỗi, logic tiếp theo sẽ được thực thi trong Controller.

Sau đó, bạn có thể sử dụng Form Request trong Controller như sau:

namespace App\Http\Controllers;

use App\Http\Requests\StoreBlogPostRequest;

class BlogController extends Controller
{
    /**
     * Store a newly created resource in storage.
     *
     * @param  \App\Http\Requests\StoreBlogPostRequest  $request
     * @return \Illuminate\Http\Response
     */
    public function store(StoreBlogPostRequest $request)
    {
        // Xử lý dữ liệu sau khi đã được validate
    }
}

Trong đoạn mã trên, chúng ta đã sử dụng Form Request StoreBlogPostRequest trong phương thức store() của Controller BlogController. Điều này sẽ giúp Laravel tự động thực hiện quy tắc validation và trả về các thông báo lỗi tương ứng nếu dữ liệu không hợp lệ. Nếu dữ liệu hợp lệ, chúng ta có thể tiếp tục xử lý dữ liệu trong phương thức store().

Để hiển thị thông báo lỗi validation, bạn có thể sử dụng phương thức messages() trong Form Request hoặc phương thức validate() trong controller. Nếu bạn sử dụng phương thức messages() trong Form Request, bạn có thể định nghĩa các thông báo lỗi tùy chỉnh cho mỗi quy tắc validation.

Ví dụ, để định nghĩa thông báo lỗi ở ví dụ trên, ta có thể sửa phương thức rules() như sau:

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class StoreBlogPostRequest extends FormRequest
{
    /**
     * Determine if the user is authorized to make this request.
     *
     * @return bool
     */
    public function authorize()
    {
        return true;
    }

    /**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => 'required|max:255',
            'body' => 'required',
        ];
    }

    /**
     * Get the error messages for the defined validation rules.
     *
     * @return array
     */
    public function messages()
    {
        return [
            'title.required' => 'Vui lòng nhập tiêu đề bài viết',
            'title.max' => 'Tiêu đề bài viết không được vượt quá :max ký tự',
            'body.required' => 'Vui lòng nhập nội dung bài viết',
        ];
    }
}

Trong ví dụ này, chúng ta đã tùy chỉnh lại các thông báo lỗi cho mỗi quy tắc validation. Ví dụ, nếu người dùng không nhập tiêu đề, Laravel sẽ trả về thông báo lỗi "Vui lòng nhập tiêu đề" thay vì "The title field is required." mặc định.

Cách để hiển thị lỗi ra view sau khi validation?

Để hiển thị các thông báo lỗi validation trong view, bạn có thể sử dụng biến $errors. Biến này là một instance của class Illuminate\Support\MessageBag và chứa các thông báo lỗi validation.

Trong view, bạn có thể sử dụng method has() để kiểm tra xem biến $errors có chứa thông báo lỗi hay không. Nếu có, bạn có thể sử dụng method first() để lấy ra thông báo lỗi đầu tiên. Ví dụ:

@if ($errors->has('title'))
    <div class="alert alert-danger">
        {{ $errors->first('title') }}
    </div>
@endif

Ở đây, chúng ta sử dụng method has('title') để kiểm tra xem có thông báo lỗi cho trường title hay không. Nếu có, chúng ta hiển thị thông báo lỗi đầu tiên cho trường này bằng cách sử dụng method first('title').

2.4 Custom Validate Rules

2.4.1 Custom Validate Rules với Rule Objects

Ở ví dụ ở trên, nếu mình muốn trường title chỉ nhận một chuỗi IN HOA thì như thế nào? Để giải quyết việc này, chúng ta sử dụng Custom Validate Rules bằng Rule Object của Laravel:

Tạo một Rule Object với tên là UppercaseRule bằng lệnh sau:

php artisan make:rule UppercaseRule

Sau khi chạy lệnh bạn sẽ nhận được file app/Rules/UppercaseRule.php. Sau đó, bạn thêm đoạn code như ở dưới:

<?php

namespace App\Rules;

use Illuminate\Contracts\Validation\Rule;

class UppercaseRule implements Rule
{
    /**
     * Create a new rule instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Determine if the validation rule passes.
     *
     * @param  string  $attribute
     * @param  mixed  $value
     * @return bool
     */
    public function passes($attribute, $value)
    {
        return (strtoupper($value) === $value);
    }

    /**
     * Get the validation error message.
     *
     * @return string
     */
    public function message()
    {
        return 'The :attribute must be Uppercase.';
    }
}

*Ở đây một Rule Object sẽ có 2 phương thức là pasessmessage.

  • Phương thức passes($attribute, $value) với $attribute là tên của thuộc tính được validate, $value là giá trị của thuộc tính đó. Phương thức này nên được return true hoặc false tùy thuộc vào việc thuộc tính $attribute có hợp lệ hay không.
  • Phương thức message() trả về thông báo lỗi khi dữ liệu không hợp lệ.

Sử dụng UppercaseRule mới vừa tạo vào Form Request:

Khai báo sử dụng UppercaseRule trong file app/Http/Requests/StoreBlogPostRequest.php

use App\Rules\UppercaseRule;

Gọi UppercaseRule trong phương thức rules()

/**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => [
                    'required',
                    'max:255',
                    new UppercaseRule(),
            ],
            'body' => 'required',
        ];
    }

Thật đơn giản để tạo một Rule Custom với Rule Object phải không nào. Tương tự như trên, bạn có thể tạo ra các Rule mà bạn mong muốn rồi đấy.

Tiếp theo chúng ta sẽ tìm hiểu thêm một cách nữa để tạo một Rule Custom.

2.4.2 Custom Validate Rules với Closures

Vẫn với ví dụ StoreBlogPostRequest ở trên

Vấn đề đặt ra: Ở đây mình muốn thêm một trường number chỉ nhận các số chia hết cho 5.

Bạn chỉ cần thêm đoạn code ở dưới vào phương thức rules() trong file app/Http/Requests/StoreBlogPostRequest.php

/**
     * Get the validation rules that apply to the request.
     *
     * @return array
     */
    public function rules()
    {
        return [
            'title' => [
                    'required',
                    'max:255',
                    new UppercaseRule(),
            ],
            'body' => 'required',
            'number' => [
                'required',
                'integer',
                function ($attribute, $value, $fail) {
                    if ((($value % 5) != 0) || ($value == 0)) {
                        return $fail('The ' . $attribute . ' must be divisible by 5');
                    }
                }
            ],
        ];
    }

*Ở đây: chúng ta sẽ sử dụng một Closure trong mảng các rule của trường number để tạo một Rule Custom thay vì sử dụng một Rule Object để tạo. Closure này sẽ nhận 3 tham số là:

  • $attribute : tên của thuộc tính được validate
  • $value: giá trị của thuộc tính đó
  • $fail: một callback mà nó sẽ được gọi khi validation thất bại. Bạn sẽ truyền thông báo mà bạn muốn trả về khi validation thất bại vào trong callback này.

Với Closure sẽ cho phép chúng ta tạo nhanh một Rule Custom hơn so với Rule Object. Tùy vào ứng dụng của bạn mà chọn cách thích hợp nhé.

Cảm ơn mọi người đã đọc, hy vọng bài viết có thể hữu ích 😄

Nguồn tham khảo:


All rights reserved

Viblo
Hãy đăng ký một tài khoản Viblo để nhận được nhiều bài viết thú vị hơn.
Đăng kí