Глобальні скоупи в Laravel: автоматичне фільтрування запитів

Перекладено ШІ
Оригінал: Laravel News
Оновлено: 13 серпня, 2025
Глобальні області в Laravel забезпечують ефективний спосіб застосування послідовних обмежень до всіх запитів для конкретних моделей Eloquent. Цей підхід, особливо корисний у багатокористувацьких програмах, допомагає зберігати цілісність даних, дозволяючи автоматично фільтрувати записи відповідно до бізнес-правил. Чи готові ви дізнатися більше про те, як глобальні області можуть спростити ваше життя як розробника? Читайте далі

Глобальні скоупи забезпечують зручний спосіб застосування однакових обмежень для всіх запитів до певних моделей Eloquent. Особливість м'якого видалення в Laravel ілюструє цю концепцію, автоматично виключаючи видалені записи з результатів запитів.

Глобальні скоупи визначаються шляхом створення класів, які реалізують інтерфейс Illuminate\Database\Eloquent\Scope:

<?php

namespace App\Models\Scopes;

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): void
    {
        $builder->where('is_active', true);
    }
}

Щоб застосувати скоуп до вашої моделі, використовуйте атрибут ScopedBy, щоб усі запити автоматично включали це обмеження:

<?php

namespace App\Models;

use App\Models\Scopes\ActiveScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
use Illuminate\Database\Eloquent\Model;

#[ScopedBy([ActiveScope::class])]
class Product extends Model
{
    protected $fillable = ['name', 'price', 'is_active'];
}

Цей підхід особливо корисний у багатокористувацьких застосунках, де критично важливою є ізоляція даних. Уявіть собі платформу SaaS, яка обслуговує кілька організацій, де дані кожного орендаря повинні залишатися повністю відокремленими:

<?php

namespace App\Models\Scopes;

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

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

class PublishedScope implements Scope
{
    public function apply(Builder $builder, Model $model): void
    {
        $builder->where('status', 'published')
                ->where('published_at', '<=', now());
    }
}

#[ScopedBy([TenantScope::class, PublishedScope::class])]
class Document extends Model
{
    protected $fillable = ['title', 'content', 'status', 'tenant_id', 'published_at'];

    protected $casts = [
        'published_at' => 'datetime',
    ];
}

class DocumentController extends Controller
{
    public function index()
    {
        $documents = Document::orderBy('published_at', 'desc')->get();

        return view('documents.index', compact('documents'));
    }

    public function adminIndex()
    {
        $allDocuments = Document::withoutGlobalScopes([PublishedScope::class])
                               ->orderBy('created_at', 'desc')
                               ->get();

        return view('admin.documents.index', compact('allDocuments'));
    }

    public function systemReport()
    {
        $systemWideDocuments = Document::withoutGlobalScopes()
                                     ->selectRaw('tenant_id, count(*) as document_count')
                                     ->groupBy('tenant_id')
                                     ->get();

        return view('reports.system', compact('systemWideDocuments'));
    }
}

Глобальні скоупи автоматично забезпечують дотримання бізнес-правил на рівні моделі, гарантуючи узгодженість даних без потреби у ручному застосуванні обмежень у кожному запиті. За необхідності, ви можете обійти ці обмеження, використовуючи withoutGlobalScope() для конкретних скоупів або withoutGlobalScopes(), щоб видалити всі застосовані глобальні фільтри