Глобальні скоупи забезпечують зручний спосіб застосування однакових обмежень для всіх запитів до певних моделей 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()
, щоб видалити всі застосовані глобальні фільтри