Laravel – Advanced Eloquent: Scopes & Global Scope

2 min read

Vì sao cần Advanced Eloquent?

Khi project lớn dần, bạn sẽ thấy:

  • Query logic lặp lại ở nhiều nơi
  • Controller chứa nhiều điều kiện filter
  • Khó maintain và khó test

Advanced Eloquent giúp bạn:

  • Tái sử dụng query
  • Tách business rule khỏi controller
  • Kiểm soát dữ liệu ở mức model

Tài liệu chính thức:
https://laravel.com/docs/eloquent


1️⃣ Local Scope – Tái sử dụng Query

Local scope giúp bạn đóng gói logic truy vấn trong model.

Ví dụ:

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

Sử dụng:

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

Thay vì:

User::where('active', true)->get();

Ưu điểm:

  • Clean controller
  • Dễ đọc
  • Tái sử dụng logic

Scope có tham số

public function scopeRole($query, $role)
{
    return $query->where('role', $role);
}
Dùng:
User::role('admin')->get();

2️⃣ Global Scope – Áp dụng tự động

Global scope tự động áp dụng cho mọi query của model.

Ví dụ soft delete (mặc định Laravel đã dùng global scope).

Ví dụ custom global scope:

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;class ActiveScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('active', true);
    }
}

Đăng ký trong model:

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

Giờ mọi query:

User::all();

Tự động thêm điều kiện active = true.


3️⃣ Multi-Tenant với Global Scope

Trong SaaS, mỗi user thuộc một tenant.

Bạn có thể tạo TenantScope:

class TenantScope implements Scope
{
    public function apply(Builder $builder, Model $model)
    {
        $builder->where('tenant_id', auth()->user()->tenant_id);
    }
}

Điều này giúp:

  • Ngăn data leak
  • Tự động filter dữ liệu

⚠ Cẩn thận khi dùng auth() trong global scope nếu chạy queue hoặc CLI.


4️⃣ Bỏ Global Scope Khi Cần

Bạn có thể bypass:

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

Hoặc:

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

Hữu ích cho admin panel hoặc report.


5️⃣ Attribute Casting Nâng Cao

Casting giúp đảm bảo kiểu dữ liệu.

protected $casts = [
    'is_active' => 'boolean',
    'settings' => 'array',
    'published_at' => 'datetime',
];

Laravel còn hỗ trợ custom cast class.

Ví dụ:

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;class JsonCast implements CastsAttributes
{
    public function get($model, $key, $value, $attributes)
    {
        return json_decode($value, true);
    }    public function set($model, $key, $value, $attributes)
    {
        return json_encode($value);
    }
}

Dùng:

protected $casts = [
    'metadata' => JsonCast::class,
];

6️⃣ Accessor & Mutator (Cách mới Laravel 9+)

Laravel hỗ trợ cú pháp mới:

use Illuminate\Database\Eloquent\Casts\Attribute;protected function fullName(): Attribute
{
    return Attribute::make(
        get: fn () => $this->first_name . ' ' . $this->last_name
    );
}

Giúp code rõ ràng và type-safe hơn.


7️⃣ Khi nào nên tách Repository?

Nếu query bắt đầu:

  • Phức tạp
  • Có nhiều join
  • Có nhiều điều kiện động

Bạn có thể tách sang:

  • Repository
  • Query object
  • Service layer

Không phải lúc nào cũng cần repository.
Chỉ dùng khi có giá trị thực sự.


8️⃣ Performance Considerations

Global scope có thể:

  • Ảnh hưởng performance nếu thêm điều kiện nặng
  • Làm query phức tạp hơn

Luôn kiểm tra:

EXPLAIN SELECT ...

Tránh:

  • Subquery không index
  • Condition không cần thiết

Kết luận

Advanced Eloquent giúp bạn:

  • Tái sử dụng query với Local Scope
  • Bảo vệ dữ liệu với Global Scope
  • Đảm bảo type với Casting
  • Viết model sạch và maintainable
Avatar photo

Leave a Reply

Your email address will not be published. Required fields are marked *