Query Scopes en Laravel
Cuando trabajamos con consultas personalizadas en Laravel, muchas veces terminamos repitiendo condiciones en varios lugares del código. Esto no solo hace que el código sea más largo, sino que también más difícil de mantener.
Para resolver esto, Laravel nos ofrece los Query Scopes (ámbitos de consulta), que nos permiten encapsular estas condiciones y reutilizarlas fácilmente.
Mini-aplicación de ejemplo
En este tutorial vamos a trabajar con una tabla de productos, donde cada producto tendrá:
un estado (
oactivo
),inactivouna categoría,
y además podremos filtrarlos por fecha de creación.
Con este ejemplo vamos a ver cómo aplicar Global Scopes y Local Scopes en Laravel.
Creación de la tabla de productos
En tu migración (
database/migrations/xxxx_xx_xx_create_products_table.php) podemos definir algo como:Schema::create('products', function (Blueprint $table) { $table->id(); $table->string('nombre'); $table->text('descripcion')->nullable(); $table->decimal('precio', 8, 2); $table->boolean('activo')->default(true); $table->string('categoria'); $table->timestamps(); });
De esta forma tendremos productos con estado y categoría para nuestros ejemplos.
Poblando la base de datos
En el ProductFactory (
database/factories/ProductFactory.php):$factory->define(App\Models\Product::class, function (Faker $faker) { return [ 'nombre' => $faker->word, 'descripcion' => $faker->sentence, 'precio' => $faker->randomFloat(2, 10, 2000), 'activo' => $faker->boolean, 'categoria' => $faker->randomElement(['Electrónica', 'Ropa', 'Hogar']), ]; });
En el seeder (
DatabaseSeeder.php):\App\Models\Product::factory(100)->create();
Y ejecutamos:
php artisan migrate --seed
Uso de un Global Scope
Imagina que en tu aplicación solo quieres trabajar con productos activos en todo el sistema.
En lugar de escribir
where('activo', true) en cada consulta, podemos usar un Global Scope.Creamos un archivo en
app/Scopes/ActiveScope.php:namespace App\Scopes; use Illuminate\Database\Eloquent\Scope; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Builder; class ActiveScope implements Scope { public function apply(Builder $builder, Model $model) { $builder->where('activo', true); } }
Y lo aplicamos en el modelo
Product:use App\Scopes\ActiveScope; protected static function boot() { parent::boot(); static::addGlobalScope(new ActiveScope); }
Ahora, cada vez que consultemos
Product::all(), solo obtendremos productos activos.Si en algún momento queremos ignorar el Global Scope:
Product::withoutGlobalScope(ActiveScope::class)->get();
Uso de un Local Scope
Los Local Scopes son útiles para condiciones que reutilizamos seguido, pero que no queremos aplicar siempre.
Ejemplo: mostrar productos de una categoría específica creados este mes.
En el modelo
Product:public function scopeDeCategoriaEsteMes($query, $categoria) { return $query->where('categoria', $categoria) ->whereMonth('created_at', now()->month); }
Y lo usamos así:
Product::deCategoriaEsteMes('Electrónica')->get();
Conclusión
Con los Global Scopes podemos aplicar filtros que afectan a todas las consultas de un modelo (como productos activos).
Con los Local Scopes, en cambio, definimos consultas reutilizables para casos específicos (como productos por categoría en un mes determinado).
De esta forma reducimos código repetitivo y hacemos nuestras consultas en Eloquent más limpias y expresivas.