Skip to main content

Qué es un scope

Un scope de Eloquent es un mecanismo que empaqueta y reutiliza restricciones aplicadas a una consulta. Existen dos tipos.

Scopes locales

Definición

Los scopes locales se declaran añadiendo el atributo #[Scope] a un método del modelo.
El atributo #[Scope] está en el namespace Illuminate\Database\Eloquent\Attributes. Es sintaxis nativa de PHP 8.0 o superior.

Uso

Los scopes se invocan como métodos. Se pueden encadenar.

Paso de parámetros

Puedes definir parámetros adicionales a partir del segundo argumento del método.
Al invocarlo, pasa los argumentos como es habitual.

Combinación con orWhere

Al conectar un scope con orWhere, a veces necesitas agrupar lógicamente.

Scopes globales

Cómo funcionan

Un scope global es una clase que implementa la interfaz Illuminate\Database\Eloquent\Scope. Esta interfaz solo requiere un método apply.
Dentro de apply añades restricciones al query builder.

Crear una clase de scope global

Genera el esqueleto con el comando make:scope.
Se crea app/Models/Scopes/ActiveScope.php.

Aplicación al modelo

1

Registro con el atributo #[ScopedBy] (recomendado)

En Laravel 13, la forma más sencilla es usar el atributo #[ScopedBy].
Puedes indicar varios scopes en el array.
2

Registro manual mediante booted()

También puedes sobrescribir el método booted y llamar a addGlobalScope.
Cuando añades un scope global, todas las consultas (como User::all()) reciben automáticamente el filtro WHERE is_active = 1.

Scope global mediante closure anónima

Para scopes simples que no merecen una clase propia, puedes definirlos con un closure.
Para excluir después un scope definido con closure, tienes que usar el nombre del scope (cadena), no un nombre de clase.

Excluir scopes globales

En algunas consultas concretas querrás desactivar el scope.

Interior del framework: SoftDeletingScope

Ver cómo el trait estándar SoftDeletes aprovecha los scopes globales revela un patrón de implementación útil. SoftDeletingScope implementa la interfaz Scope.
withTrashed() en realidad llama a withoutGlobalScope($this). Es decir, se excluye a sí mismo (SoftDeletingScope) para que los registros eliminados se puedan recuperar.
De forma parecida, onlyTrashed() se excluye a sí mismo y añade whereNotNull('deleted_at').
La interfaz Scope no define el método extend, pero el builder de Eloquent lo llama automáticamente si el scope lo tiene. Es útil cuando quieres añadir macros propias.

Casos de uso prácticos

Multi-tenant: filtrado automático por ID de tenant

En una aplicación SaaS es crítico aplicar el filtro por tenant a todas las consultas de forma automática.
Con esto, al llamar a Post::all() solo se devuelven los datos del tenant del usuario autenticado.

Filtros de publicación

En el panel de administración quieres ver también los posts no publicados, pero en el frontend solo los ya publicados.
En el panel de administración se excluye el scope con withoutGlobalScope.

Usa addSelect en lugar de select

Cuando añadas columnas dentro de un scope global, utiliza addSelect en lugar de select. Si usas select, sobrescribirás las columnas que la consulta original haya seleccionado.

Próximos pasos

Casts personalizados de Eloquent

Aprende a implementar la lógica de conversión de atributos como casts personalizados y a aprovechar el patrón Value Object.
Última modificación el 13 de julio de 2026