Bài 13: Form Request trong Laravel - Học lập trình Laravel

Đăng bởi: Admin | Lượt xem: 5161 | Chuyên mục: Laravel


1. Form Request là gì?

Form Request chỉ đơn giản là các class được tạo ra phục vụ cho mục đích chứa các mã nghiệp vụ kiểm tra dữ liệu và phân quyền với các HTTP request
Tạo Form Request bằng câu lệnh artisan 
Các class Form Request có thể được tạo ra bằng câu lệnh artisan make:request 
php artisan make:request StoreBlogPost
Khi đó, một class StoreBlogPost sẽ được tạo ra trong thư mục app\Http\Requests. Chú ý, nếu thư mục này chưa tồn tại bạn cũng đừng lo vì câu lệnh artisan trên sẽ tự động tạo ra nếu chưa có.
Kiểm tra dữ liệu bằng phương thức rules
Trong class Form Request sẽ có hai phương thức được đưa vào sẵn là authorize() và rules(). Phương thức rules() được sử dụng để kiểm tra dữ liệu được nhập vào từ HTTP request, ví dụ kiểm tra như sau:
/**
 * Get the validation rules that apply to the request.
 *
 * @return array
 */
public function rules()
{
    return [
        'title' => 'required|unique:posts|max:255',
        'body' => 'required',
        'publication_date' => 'date',
    ];
}
Chúng ta đã định nghĩa xong các rule, nhưng bằng cách nào các rule này được sử dụng để kiểm tra. Trong phương thức của Controller chúng ta sẽ sử dụng type-hint của Form Request sử dụng. Form request sẽ được sử dụng để validate trước khi phương thức trong Controller được gọi, như vậy code trong Controller không còn lộn xộn với các logic kiểm tra nữa:
/**
 * Store the incoming blog post.
 *
 * @param  StoreBlogPost  $request
 * @return Response
 */
public function store(StoreBlogPost $request)
{
    // The incoming request is valid...
}
Nếu kiểm tra dữ liệu không qua được, một response chuyển hướng được sinh ra và gửi ngược trở lại cho người dùng cùng với các thông báo lỗi. Nếu request ở dạng AJAX thì một HTTP response với mã trạng thái 422 sẽ được trả về cùng với dữ liệu dạng JSON chứa các lỗi kiểm tra.
Xử lý công việc sau khi kiểm tra dữ liệu
Trong tình huống bạn muốn có những xử lý logic sau khi kiểm tra dữ liệu được thực hiện, bạn có thể sử dụng hook after trong Form Request với phương thức withValidator. Phương thức này nhận constructed validator cho phép bạn gọi bất kỳ phương thức nào của nó trước khi các validation rule được sử dụng.
/**
 * Configure the validator instance.
 *
 * @param  \Illuminate\Validation\Validator  $validator
 * @return void
 */
public function withValidator($validator)
{
    $validator->after(function ($validator) {
        if ($this->somethingElseIsInvalid()) {
            $validator->errors()->add('field', 'Something is wrong with this field!');
        }
    });
}
Mã logic phân quyền trong Form Request
Các class Form Request cũng chứa một phương thức authorize, trong phương thức này chúng ta có thể kiểm tra một người dùng được xác thực có thẩm quyền thực hiện công việc của Controller này không? Ví dụ, bạn muốn xác định một người dùng có quyền cập nhật bình luận trong bài viết của họ không?
/**
 * Determine if the user is authorized to make this request.
 *
 * @return bool
 */
public function authorize()
{
    $comment = Comment::find($this->route('comment'));

    return $comment && $this->user()->can('update', $comment);
}
Nếu phương thức authorize trả về false, một HTTP request với mã trạng thái 403 sẽ được tự động trả về và phương thức trong Controller sẽ không được thực hiện. Nếu bạn có kế hoạch thực hiện các logic phân quyền trong một phần khác của ứng dụng, bạn chỉ cần trả về true trong phương thức authorize:
/**
 * Determine if the user is authorized to make this request.
 *
 * @return bool
 */
public function authorize()
{
    return true;
}
Quản lý lỗi truy nhập vào tài nguyên không được phân quyền 
Phương thức forbiddenResponse trong class Form Request được sử dụng để xử lý khi một người dùng truy cập vào một tài nguyên không được phân quyền. Ví dụ, một người dùng không thể sửa comment không thuộc về mình, ví dụ comment này có id là 100 thì khi truy nhập vào http://hoclaptrinh.com/comment/100 nó sẽ sinh ra một exception, chúng ta muốn hiển thị rằng bình luận này thuộc về người dùng khác và thông báo người dùng không có quyền thực hiện bằng cách sử dụng phương thức forbiddenResponse().
public function forbiddenResponse()
{
    return response()->view('errors.403');
}
Bạn cần tạo ra một view trong thư mục resources\views\errors có tên là 403.blade.php, như vậy mỗi khi thực hiện một phương thức của Controller có sử dụng Form Request này, nếu người dùng không được cấp phép thực hiện nó sẽ hiển thị view 403 này. Tương tự nếu bạn muốn xử lý khi kiểm tra dữ liệu không qua được, bạn có thể dùng phương thức response().
Xây dựng validator riêng
<?php
...
class FriendFormRequest extends FormRequest
{
    public function validator(ValidationService $service)
    {
        $validator = $service->getValidator($this->input());

        // Optionally customize this version using new ->after()
        $validator->after(function() use ($validator) {
            // Do more validation

            $validator->errors()->add('field', 'new error');
        });
    }
}
Một số các thuộc tính trong Form Request
Trong các class Form Request có một số thuộc tính bạn có thể khai báo với các mục đích khác nhau:
  1. $redirect: đường dẫn sẽ chuyển hướng đến nếu kiểm tra dữ liệu không qua được.
  2. $redirectRoute: route sẽ chuyển hướng đến nếu kiểm tra dữ liệu không qua được.
  3. $redirectAction: controller action sẽ chuyển hướng đến nếu kiểm tra dữ liệu không qua được.
  4. $dontFlash: Các dữ liệu nhập trong các trường bạn không muốn flash trực tiếp (mặc định: ['password', 'password_confirmation'])

2. Ví dụ với Form Request

Chúng ta cùng xem xét một ví dụ sử dụng Form Request để xử lý các công việc kiểm tra dữ liệu theo cách riêng. Trong ví dụ này chúng ta quản lý việc tạo ra các bài viết trong một website, bài viết có tiêu đề, nội dung và ngày đăng bài. Theo xử lý thông thường, controller sẽ truy nhập vào các tham số của HTTP request, kiểm tra chúng và dựa vào kết quả đó để quyết định các hành động tiếp theo như thông báo lỗi nếu không vượt qua được kiểm tra dữ liệu hoặc tạo ra bài viết trong database nếu việc kiểm tra dữ liệu hoàn thành. Trong ví dụ này, chúng ta sẽ thêm vào một quy tắc kiểm tra dữ liệu riêng là tiêu đề và nội dung bài viết phải không chứa các từ ngữ vi phạm thuần phong mỹ tục.
Đầu tiên chúng ta tạo ra một class Form Request với câu lệnh artisan:
php artisan make:request CreatePostFormRequest
Câu lệnh này sẽ tạo ra class CreatePostFormRequest.php trong thư mục app\Http\Requests
<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;

class CreatePostFormRequest extends FormRequest
{
    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'title' => 'required|alpha_num|max:70',
            'body' => 'required|alpha_num',
            'public_date' => 'required|date'
        ];
    }
}
Trên đây là các quy tắc cơ bản để kiểm tra dữ liệu nhập vào, với yêu cầu kiểm tra xem các nội dung không chứa các từ ngữ vi phạm thuần phong mỹ tục chúng ta sẽ xem xét ở phần tiếp theo. Để sử dụng class Form Request này chúng ta chỉ đơn giản sử dụng type-hint trong tham số của controller:
<?php

namespace App\Http\Controllers;

use App\Http\Requests\CreatePostFormRequest;

class PostController extends Controller
{
    public function store(CreatePostFormRequest $request)
    {
        // ...
    }
}
Như vậy các request đến route post.store sẽ được kiểm tra với các quy tắc được định nghĩa trong CreatePostFormRequest với tiêu đề là dạng chuỗi số hoặc chữ dài tối đa 70 ký tự, nội dung là bắt buộc và có dạng chữ hoặc số, ngày đăng cũng bắt buộc và có dạng dữ liệu ngày tháng. Các request được kiểm tra sai sẽ tự động chuyển hướng ngược lại trang nhập liệu với lỗi cụ thể. Như vậy code trong controller có thể tập trung vào các kịch bản nhập liệu.
Framework Laravel đã tạo sẵn rất nhiều các phương thức kiểm tra, nhưng trong những tình huống cụ thể có thể chúng ta cần những cách thức kiểm tra đặc biệt mang tính cá nhân. Việc mở rộng rất đơn giản bằng cách đưa vào phương thức khởi tạo trong các class Form Request. Phần tiếp theo chúng ta sẽ thực hiện kiểm tra xem nội dung của tiêu đề có chứa các từ ngữ bị cấm hay không?
<?php

namespace App\Http\Requests;

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

class CreatePostFormRequest extends FormRequest
{
    // dirty_text là mảng chứa các từ không phù hợp, chỉ mang tính ví dụ.
    protected $dirty_text = ['vcl', 'sml'];
    public function __construct(Factory $factory)
    {
        $name = 'is_clean_text';
        $test = function ($_, $value, $_) {
            $str = preg_replace('/\s+/', ' ', $value);
            $word_arr = explode(" ", $str);
            foreach($word_arr as $word){
                if(isset($this->dirty_text[$word])){
                    return true;
                }
            }
            return false;
        };
        $errorMessage = 'Nội dung không được chứa các từ ngữ vi phạm thuần phong mỹ tục.';

        $factory->extend($name, $test, $errorMessage);
    }

    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'title' => 'required|alpha_num|max:70|is_clean_text',
            'body' => 'required|alpha_num',
            'public_date' => 'required|date',
        ];
    }
}
Như vậy chúng ta đã thực hiện được một quy tắc kiểm tra riêng, tuy nhiên cách thức đưa vào phương thức khởi tạo như vậy sẽ rất khó để sử dụng lại cũng như rất khó khăn khi thực hiện unit testing. Chúng ta cần tạo ra các lớp Validator để có thể sử dụng lại. Trước hết chúng ta tạo một interface CustomValidator để các lớp validator cá nhân có thể thực hiện từ interface này.
<?php

namespace App\Http\Validations;

interface CustomValidation
{
    public function name();
    public function test();
    public function errorMessage();
}
Khi đó chúng ta tạo ra một class validator với quy tắc lọc các từ khóa vi phạm thuần phong mỹ tục.
<?php

namespace App\Http\Validations;

use CustomValidation;

class CleanText implements CustomValidation
{
    public function name()
    {
        return 'is_clean_text';
    }

    public function test()
    {
        return function ($_, $value, $_) {
            $str = preg_replace('/\s+/', ' ', $value);
            $word_arr = explode(" ", $str);
            foreach($word_arr as $word){
                if(isset($this->dirty_text[$word])){
                    return true;
                }
            }
            return false;
        }
    }

    public function errorMessage()
    {
        return 'Nội dung không được chứa các từ ngữ vi phạm thuần phong mỹ tục.';
    }
}
Như vậy khi cần sử dụng quy tắc is_clean_text chúng ta chỉ cần đưa vào như sau:
<?php

namespace App\Http\Requests;

use App\Http\Validations\CleanText;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Factory;

class CreatePostFormRequest extends FormRequest
{
    public function __construct(Factory $factory)
    {
        $this->useCustomValidations($factory, $this->applicableValidations());
    }

    public function authorize()
    {
        return true;
    }

    public function rules()
    {
        return [
            'title' => 'required|alpha_num|max:70|is_clean_text',
            'body' => 'required|alpha_num|is_clean_text',
            'public_date' => 'required|date',
        ];
    }

    private function applicableValidations()
    {
        return collect([
            new CleanText(),
            // thêm các class Validator khác vào đây
        ]);
    }

    private function useCustomValidations($factory, $validations)
    {
        $validations->each(function ($validation) use ($factory) {
            $factory->extend($validation->name(), $validation->test(), $validation->errorMessage());
        });
    }
}
Bài tiếp theo: Middleware trong Laravel >>
vncoder logo

Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!