Scopes Eloquent : global scopes et local scopes
DOG&DEV · 25/01/2025
Scopes Eloquent : global scopes et local scopes
Les scopes Eloquent permettent d’encapsuler des contraintes de requêtes (ex. is_active = true, deleted_at IS NULL, tenant_id = ?) pour les réutiliser et composer sans duplication. On distingue les global scopes (appliqués à toutes les requêtes du modèle) et les local scopes (appliqués sur appel : scopeActive(), scopeRole(), etc.). Ce guide couvre la création, l’enregistrement et le contournement avec withoutGlobalScope.
Prérequis
- Projet Laravel avec Eloquent
- Modèles avec colonnes utilisées dans les scopes (
is_active,tenant_id,published_at, etc.)
1. Global scope (classe)
Un global scope est une classe qui implémente Illuminate\Database\Eloquent\Scope.
Création :
php artisan make:scope ActiveScope
Implémentation :
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);
}
}
Enregistrement sur le modèle :
use App\Models\Scopes\ActiveScope;
class User extends Model
{
protected static function booted(): void
{
static::addGlobalScope(new ActiveScope);
}
}
Dès lors, User::all(), User::find(1), User::count(), etc. incluent WHERE is_active = true.
2. Désactiver un global scope
Un scope précis :
User::withoutGlobalScope(ActiveScope::class)->get();
Tous les global scopes :
User::withoutGlobalScopes()->get();
Indispensable pour admin, jobs, audits ou exports qui doivent voir les enregistrements « inactifs » ou hors périmètre.
3. Global scope anonyme
Pour une règle simple et propre au modèle :
protected static function booted(): void
{
static::addGlobalScope('published', function (Builder $builder) {
$builder->where('published_at', '<=', now());
});
}
Désactivation : withoutGlobalScope('published').
4. Local scope (méthode)
Un local scope est une méthode scope + Nom sur le modèle. Il n’est appliqué que lorsqu’on l’appelle.
Définition :
use Illuminate\Database\Eloquent\Builder;
class User extends Model
{
public function scopeActive(Builder $query): Builder
{
return $query->where('is_active', true);
}
public function scopeRole(Builder $query, string $role): Builder
{
return $query->where('role', $role);
}
}
Utilisation :
User::active()->get();
User::active()->role('admin')->get();
User::role('editor')->where('created_at', '>', now()->subMonth())->get();
5. Local scope qui retire un global scope
Parfois un local scope doit contourner un global (ex. « avec inactifs ») :
public function scopeWithInactive(Builder $query): Builder
{
return $query->withoutGlobalScope(ActiveScope::class);
}
User::withInactive()->get();
6. Combiner global et local scopes
User::active()->role('editor')->latest()->paginate(20);
Le global applique la règle de visibilité ; les locaux filtrent selon le métier. Architecture typique pour une app qui grossit.
Quand ne pas utiliser de scopes
- Logique métier complexe (workflows, calculs) → Services, Actions.
- Décisions d’autorisation → Gates, Policies.
- Orchestration entre plusieurs modèles → Services, Jobs.
Les scopes restent des contraintes de requêtes réutilisables, pas du domaine applicatif.
Dépannage
| Symptôme | Cause possible | Correctif |
|---|---|---|
| Des lignes « inactives » apparaissent | Global scope non enregistré ou désactivé | Vérifier booted() et les appels withoutGlobalScope |
scopeActive inconnu |
Faute de nom (scopeactive, ScopeActive) |
Méthode scopeActive ; appel User::active() |
| Conflit entre scopes | Ordre ou logique where contradictoire |
Vérifier les where dans les scopes et les combiner (and/or) de façon cohérente |
Bonnes pratiques
- Global pour les contraintes obligatoires (soft delete, multi-tenant,
is_active). - Local pour les filtres optionnels (role, statut, plage de dates).
- Scopes nommés de façon claire (
scopeActive,scopePublished,scopeForTenant).
Ressources
Cet article s’inscrit dans notre série de guides technique et développement web. Pour un serveur ou une application sur-mesure, contact.