Toidicode.com

Toidicode.com

BASIC TO ADVANCE

Bài 31: Query Scope trong Eloquent Model Laravel 8

Phần trước mình đã giới thiệu với mọi người về query trong eloquent rồi, phần này mình sẽ tiếp tục giới thiệu với mọi người về Scope trong eloquent Model.

Scope trong Eloquent model thực ra là một phương thức chứa logic query và từ đó chúng ta có thể add vào một query, tất cả query,... mà chúng ta muố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.

VD: Mình sẽ tạo ra một 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.

VD: 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 globel scope.

Nếu việc tạo thêm scope class làm bạn thấy khó chịu thì 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);
}

Trong đó:

  • $scopeName là tên của scope bạn muốn thêm. Nếu bạn cảm thấy không cần thiết có thể bỏ tham số này đi.
  • Closure là một function chứa logic của bạn.

VD: Mình sẽ chuyển scope ancient về dạng closure.

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

Trong một số trường hợp đặc biệt, 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.

VD: Bỏ Ancient scope 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ị.

VD:

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.

VD: Loại bỏ hết global scope của model trong query hiện tại.

use App\Models\User;

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

3. Local scope.

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

VD: Đị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
{
    /**
     * 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);
    }
}

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).

VD:

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).

VD:

<?php

namespace App\Models;

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);
    }
}

Truy vấn scope:

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

4. Lời kết.

Phần scope này sẽ rất hữu dụng nếu như các bạn biết cách áp dụng nó vào trong dự án.

Đăng ký nhận tin.

Chúng tôi chỉ gửi tối đa 2 lần trên 1 tháng. Tuyên bố không spam mail!

Vũ Thanh Tài

About author
The best way to learn is to share
Xem tất cả bài đăng

0 Comments

Bài viết chưa có ai bình luận, hãy là người đầu tiên đi bạn!

Bình luận

Captcha