Laravel Tutorial

Bài 29: Query Scope trong Eloquent Model

Scope trong Eloquent model là phương thức hỗ trợ ta thêm 1 điều kiện nào đó để quá trình query diễn ra gọn gàng nhanh chóng hơn

1. Global scope

Global scope trong Eloquent cho phép chúng ta đưa thêm cách query khác vào trong tất cả query của model

Ví dụ như soft delete mình đã giới thiệu ở phần trước cũng là một global scope, soft delete add thêm điều kiện deleted is null vào mọi query để cho chúng ta chỉ nhận được những bản ghi chưa được xóa mềm

Để tạo mới một global scope trong Laravel rất là đơn giản, chúng ta chỉ cần tạo ra một class implement interface Illuminate\Database\Eloquent\Scope , laravel chưa có bất kì một quy định nào cho việc đặt file scope ở đâu. Nhưng ở đây theo quan điểm cá nhân thì mình sẽ đặt nó ở app/Scopes

Khi implement interface Illuminate\Database\Eloquent\Scope thì bắt buộc chúng ta phải khai báo lại phương thức apply

Ví dụ: Tạo scope để loại bỏ các bản ghi được tạo ra quá một năm trước có tên là AncientScope

app/Scopes/AncientScope.php

<?php 

namespace App\Scopes; 

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

class AncientScope 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('created_at', '<', now()->subYear());
    }
}

Lúc này, để apply golobal scope AncientScope vào trong model nào đó thì bạn chỉ việc khai báo chúng vào trong phương thức booted với cú pháp:

protected static function booted()
{
    static::addGlobalScope(new ScopeName());
}

Trong đóScopeName là tên của scope các bạn muốn sử dụng

Ví dụ: Sử dụng AncientScope scope trong User model

<?php

namespace App\Models;

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

class User extends Model
{
    /**
     * The "booted" method of the model.
     *
     * @return void
     */
    protected static function booted()
    {
        static::addGlobalScope(new AncientScope());
    }
}

2. Anonymous global scope

Ngoài cách tạo thêm scope class, bạn có thể truyền vào phương thức addGlobalScope một closure với cú pháp:

protected static function booted()
{
    static::addGlobalScope($scopeName, Closure);
}

Ví dụ: chuyển scope ancient về dạng closure

static::addGlobalScope('ancient', function ($builder) {
    return $builder->where('created_at', '<', now()->subYear());
});

Nếu như bạn không muốn apply global scope nào đó vào trong câu query hiện tại bạn có thể sử dụng phương thức withoutGlobalScope

Ví dụ: Bỏ scope ancient ra khỏi query hiện tại

use App\Models\User;

// Đối với scope là class.
User::withoutGlobalScope(AncientScope::class)->get();

// Đối với scope là closure.
User::withoutGlobalScope('ancient')->get();

Nếu bạn muốn loại bỏ nhiều global scope một lúc bạn có thể truyền vào phương thức withoutGlobalScope một mảng giá trị

use App\Models\User;

User::withoutGlobalScopes([
    FirstScope::class, SecondScope::class
])->get();

Hoặc bạn có thể loại bỏ tất cả các global scope trong model bằng cách không truyền scope name vào trong phương thức withoutGlobalScopes

use App\Models\User;

User::withoutGlobalScopes()->get();

3. Local scope

Khác với global scope, local scope là một phương thức có tên được bắt đầu bằng “scope” và chứa code nằm trong model. Từ đó chúng ta có thể dễ dàng truy vấn nó trong builder

Ví dụ: Định nghĩa scopePopular để query các user có votes lớn hơn 100

<?php 

namespace App\Models; 

use Illuminate\Database\Eloquent\Model; 

class User extends Model 
{ 
    public function scopePopular($query) { 
        return $query->where('votes', '>', 100);
    }
    
    public function scopeActive($query)
    {
        return $query->where('active', 1);
    }
}

Lưu ý: Local scope bắt buộc phải return về một query builder

Lúc này bạn muốn sử dụng scope nào bạn chỉ cần gọi tên scope đó (bỏ chữ “scope” và bắt đầu bằng kí tự in thường)

use App\Models\User;

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

Nếu như bạn muốn truyền thêm tham số vào trong scope thì bạn chỉ cần định nghĩa thêm các param vào sau param $query và đến lúc truy vấn thì bạn truyền các tham số vào theo thứ tự bình thường (bỏ qua param $query)

<?php 

namespace App\Models; 

use Illuminate\Database\Eloquent\Model; 

class User extends Model 
{ 
    public function scopeOfType($query, $type) {         
        return $query->where('type', $type);    
    }
}

Truy vấn scope:

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

 

Leave a Comment