Глобальний скоп додає умову до всіх запитів моделі автоматично. SoftDeletes - саме такий скоп: він дописує where deleted_at is null, і тому видалені записи не з'являються у вибірках.
Свій скоп через атрибут:
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
#[ScopedBy(PublishedScope::class)]
class Post extends Model
{
}
class PublishedScope implements Scope
{
public function apply(Builder $builder, Model $model): void
{
$builder->where('is_published', true);
}
}
Зняти на один запит:
Post::withoutGlobalScope(PublishedScope::class)->get();
Post::withoutGlobalScopes()->get();
У чому небезпека. Умова застосовується там, де її ніхто не бачить у коді виклику:
- Адмінка показує не все. Редактор відкриває список і не бачить чернеток, бо скоп мовчки їх відрізав. Помилка виглядає як зникнення даних.
- Підрахунки брешуть.
Post::count()рахує лише опубліковані, і звіт розходиться з базою без видимої причини. - Зв'язки теж під скопом.
$user->postsвіддає відфільтроване, і зовні це не помітно. updateOrCreateможе створити дубль. Пошук не знаходить існуючий запис, бо той відрізаний скопом, і замість оновлення відбувається вставка.
Практичне правило: глобальний скоп доречний, коли умова справді інваріант для всієї моделі - як SoftDeletes чи розділення по тенантах. Для «зазвичай потрібні лише опубліковані» краще локальний скоп: він явний у місці виклику.
public function scopePublished(Builder $query): void
{
$query->where('is_published', true);
}
Post::published()->get(); // видно, що фільтр застосовано