Scope trong Laravel 5.5

Global Scopes

Global Scopes cho phép bạn tạo các ràng buộc cho TẤT CẢ các truy vấn tới Model. Trong Laravel, chức năng soft delete sử dụng như một global scopes để thực hiện việc pull "non-deleted" từ các model. Viết một global scopes cho mình sẽ có thể cung cấp sự tiện lợi, dễ dàng , để đảm bảo cho mỗi truy vấn tới Model đều nhận được sự ràng buộc nhất định.

Viết Global Scopes

Việc viết 1 global scopes khá đơn giản. Ta sẽ định nghĩa 1 class, class này sẽ implements từ Illuminate\Database\Eloquent\Scope interface. Interface này sẽ yêu cầu bạn implements một phương thức : apply. Phương thức apply có thể thêm where trong truy vấn của bạn nếu bạn cần sử dụng nó:

<?php

namespace App\Scopes;

use Illuminate\Database\Eloquent\Scope;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class AgeScope implements Scope
{
    /**
     * Apply the scope to a given Eloquent query builder.
     *
     * @param  \Illuminate\Database\Eloquent\Builder  $builder
     * @param  \Illuminate\Database\Eloquent\Model  $model
     * @return void
     */
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('age', '>', 200);
    }
}

Sử dụng

Để đăng kí 1 Global Scopes cho một model, bạn nên thực hiện việc override(ghi đè) lên phương thức boot, sử dụng phương thức addGlobalScope:

<?php

namespace App;

use App\Scopes\AgeScope;
use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope(new AgeScope);
    }
}

Sau khi đăng kí, truy vấn của bạn sẽ có sự thay đổi theo cách mà bạn đã tạo ra một Global Scopes trong Model. Ví dụ như User::all() sẽ được hiểu là:

select * from `users` where `age` > 200 

Anonymous Global Scopes

Eloquent cũng cho phép bạn định nghĩa một Global Scopes sử dụng Closures, điều này khá hữu ích cho các scope đơn giản mà không cần tạo class riêng biệt :

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;

class User extends Model
{
    /**
     * The "booting" method of the model.
     *
     * @return void
     */
    protected static function boot()
    {
        parent::boot();

        static::addGlobalScope('age', function (Builder $builder) {
            $builder->where('age', '>', 200);
        });
    }
}

Xóa một Global Scopes

Nếu bạn muốn xóa một Global Scopes, bạn hãy sử dụng phương thức withoutGlobalScope. Phương thức này sẽ chấp nhận tên class của Global Scopes là tham số duy nhất:

 User::withoutGlobalScope(AgeScope::class)->get();

Nếu bạn muốn xóa bỏ một vài Global Scope hoặc thậm chí tất cả Global Scopes, sử dụng phương thức withoutGlobalScopes :

// Remove all of the global scopes...
User::withoutGlobalScopes()->get();

// Remove some of the global scopes...
User::withoutGlobalScopes([
    FirstScope::class, SecondScope::class
])->get();

Local Scopes

Local Scopes cho phép bạn định nghĩa tập hợp các ràng buộc chung mà bạn có thể tái sử dụng trong ứng dụng của bạn. Ví dụ bạn có thể cần phải thường xuyên truy xuất tất cả người dùng mà được gọi là "popular users". Để định nghĩa một scope, bạn cần đặt tiền tố scope trong một hàm của Eloquent model. Scope luôn trả về một instance của Query Builder. VD:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Scope a query to only include popular users.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }

    /**
     * Scope a query to only include active users.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

Sử dụng

Với mỗi scope đã được định nghĩa , bạn có thể gọi phương thức của scope khi truy vấn đến model. Tuy nhiên, bạn không nên include tiền tố scope khi mà gọi đến phương thức đó. Bạn cũng sử dụng các truy vấn hay điều kiện như bình thường.

$users = App\User::popular()->active()->orderBy('created_at')->get();

Dynamic Scopes

Đôi khi, bạn cũng muốn định nghĩa một scope chấp nhận các tham số. Để bắt đầu, thêm các tham số vào scope của bạn. Các tham số của Scope nên được định nghĩa sau tham số $query. Ví dụ:

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    /**
     * Scope a query to only include users of a given type.
     *
     * @param \Illuminate\Database\Eloquent\Builder $query
     * @param mixed $type
     * @return \Illuminate\Database\Eloquent\Builder
     */
    public function scopeOfType($query, $type)
    {
        return $query->where('type', $type);
    }
}

Giờ đây, bạn có thể truyền tham số khi gọi đến Scope:

$users = App\User::ofType('admin')->get();

Kết Luận

Qua bài viết này, hi vọng các bạn có thể hiểu được lợi ích của Scope cũng như áp dụng được Scope trong Laravel vào project của mình. Mong nhận được ủng hộ của các bạn. Mình xin kết thúc bài viết ở đây. Bài viết dựa trên trang chủ của Laravel. Tham khảo tại : Query Scope trong Laravel 5.5.