Tập 10: VI. Route model binding (ok)

https://viblo.asia/p/tap-10-routing-laravel-Qpmlew4oKrd

VI. Route model binding

Khi bạn inject một model instance theo ID nào đó vào route hoặc controller action, thông thường ta sẽ phải truy vấn đến model theo ID đã cho. Nhưng Laravel route model binding cung cấp cho chúng ta một cú pháp thoải mái để có thể tự động inject các model object trong route. Tức là thay vì chỉ inject ID của User rồi mới khởi tạo model thì ta sẽ inject luôn cả model object thông qua ID nhận từ tham số URI.

1. Binding ngầm (Implicit binding)

Laravel sẽ tự động resolve model được định nghĩa trong route hoặc controller action bằng cách type-hint và khai báo biến có tên trùng với tên tham số.

Route::get('api/users/{user}', function (App\User $user) {
    return $user->email;
});

Đoạn code trên có nghĩa là khi chúng ta truy cập đường dẫn http://localhost:8000/api/user/1 chẳng hạn thì một model object sẽ được khởi tạo với ID bằng 1 từ database, sau đó inject vào route và trả về $user->email. Nếu không tồn tại user với ID bằng 1, thì ta sẽ nhận kết quả là lỗi 404.

Vì chúng ta chưa tìm hiểu đến "Model Laravel" nên không thể test cho các bạn xem được. Các bạn có thể tự kiểm chứng sau khi tới tập đó nhé!

Mặc định thì route model binding sẽ dùng ID để truy vấn vào database. Bạn có thể thay đổi thiết lập này bằng cách khai báo method getRouteKeyName trong model mà bạn muốn thay đổi.

/**
 * Get the route key for the model.
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'username';
}

2. Binding rõ ràng (Explitcit binding)

Nếu bạn muốn code trở nên rõ ràng, có thể sử dụng explitcit binding trong RouteServiceProvider tại boot bằng cách sử dụng method Route::model.

public function boot()
{
    parent::boot();

    Route::model('user', App\User::class);
}

Trong đó:

  • Tham số thứ nhất sẽ là tên tham số URI

  • Tham số thứ hai là class model.

Sau đó inject bình thương như implitcit binding:

Route::get('profile/{user}', function (App\User $user) {
    // 
});

Nếu bạn muốn sử dụng cách xử lý logic riêng, bạn có thể sử dụng phương thức Route::bind. Closure object được truyền vào sẽ nhận giá trị của tham số trên URI và sẽ trả về model object cần để inject nếu thỏa mãn điều kiện mà bạn đưa ra.

public function boot()
{
    parent::boot();

    Route::bind('user', function ($value) {
        return App\User::where('name', $value)->first() ?? abort(404);
    });
}

Ở dòng return App\User::where('name', $value)->first() ?? abort(404); bạn không cần phải hiểu quá sâu sắc, chỉ là xử lý logic kiểm tra xem có tồn tại user có trường name bằng giá trị $value không, nếu có thì return model object của user đó, còn không thì trả về lỗi 404.

Hình thức này áp dụng khi bạn muốn thay vì báo lỗi 404 thì thay thế/thêm một hành động nào đó thông qua sử dụng tùy chỉnh logic riêng này.

Ngoài ra nếu bạn không muốn code quá nhiều trong RouteServiceProvider, bạn có thể định nghĩa xử lý logic riêng này vào model class mà bạn muốn thông qua method resolveRouteBinding.

/**
 * Retrieve the model for a bound value.
 *
 * @param  mixed  $value
 * @return \Illuminate\Database\Eloquent\Model|null
 */
public function resolveRouteBinding($value)
{
    return $this->where('name', $value)->first() ?? abort(404);

Last updated