1. Eloquent ORM
  2. Eloquent: Primeros pasos

Únete a nuestra comunidad de Telegram @webblend!

Aquí encontrarás fragmentos de código de Laravel y consejos útiles sobre desarrollo web.

Introducción

Laravel incluye Eloquent, un mapeador objeto-relacional (ORM) que hace que sea agradable interactuar con tu base de datos. Al usar Eloquent, cada tabla de base de datos tiene un "Modelo" correspondiente que se utiliza para interactuar con esa tabla. Además de recuperar registros de la tabla de base de datos, los modelos Eloquent te permiten insertar, actualizar y eliminar registros de la tabla también.

Nota Antes de empezar, asegúrate de configurar una conexión de base de datos en el archivo de configuración config/database.php de tu aplicación. Para obtener más información sobre la configuración de tu base de datos, consulta la documentación de configuración de base de datos.

Campo de Entrenamiento Laravel

Si eres nuevo en Laravel, siéntete libre de sumergirte en el Laravel Bootcamp. El Laravel Bootcamp te guiará en la construcción de tu primera aplicación Laravel utilizando Eloquent. Es una excelente manera de conocer todo lo que Laravel y Eloquent tienen para ofrecer.

Generación de Clases de Modelo

Para empezar, creemos un modelo Eloquent. Los modelos suelen estar en el directorio app\Models y extienden la clase Illuminate\Database\Eloquent\Model. Puedes usar el comando make:model Artisan para generar un nuevo modelo:

php artisan make:model Flight

Si deseas generar una migración de base de datos al generar el modelo, puedes usar la opción --migration o -m:

php artisan make:model Flight --migration

Puedes generar varios tipos de clases al generar un modelo, como fábricas, sembradoras, políticas, controladores y solicitudes de formularios. Además, estas opciones se pueden combinar para crear varias clases a la vez:

# Generate a model and a FlightFactory class...
php artisan make:model Flight --factory
php artisan make:model Flight -f
 
# Generate a model and a FlightSeeder class...
php artisan make:model Flight --seed
php artisan make:model Flight -s
 
# Generate a model and a FlightController class...
php artisan make:model Flight --controller
php artisan make:model Flight -c
 
# Generate a model, FlightController resource class, and form request classes...
php artisan make:model Flight --controller --resource --requests
php artisan make:model Flight -crR
 
# Generate a model and a FlightPolicy class...
php artisan make:model Flight --policy
 
# Generate a model and a migration, factory, seeder, and controller...
php artisan make:model Flight -mfsc
 
# Shortcut to generate a model, migration, factory, seeder, policy, controller, and form requests...
php artisan make:model Flight --all
 
# Generate a pivot model...
php artisan make:model Member --pivot
php artisan make:model Member -p

Inspección de Modelos

A veces puede ser difícil determinar todas las atributos y relaciones disponibles de un modelo solo con mirar su código. En su lugar, prueba el comando Artisan model:show, que proporciona una descripción conveniente de todos los atributos y relaciones del modelo:

php artisan model:show Flight

Convenciones del Modelo Eloquent

Los modelos generados por el comando make:model se colocarán en el directorio app/Models. Veamos una clase de modelo básica y discutamos algunas de las convenciones clave de Eloquent:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
// ...
}

Nombres de Tablas

Después de echar un vistazo al ejemplo anterior, es posible que hayas notado que no le dijimos a Eloquent qué tabla de base de datos corresponde a nuestro modelo Flight. Por convención, se utilizará el nombre "snake case" en plural de la clase como el nombre de la tabla a menos que se especifique otro nombre explícitamente. Entonces, en este caso, Eloquent asumirá que el modelo Flight almacena registros en la tabla flights, mientras que un modelo AirTrafficController almacenaría registros en una tabla air_traffic_controllers.

Si la tabla de base de datos correspondiente a tu modelo no sigue esta convención, puedes especificar manualmente el nombre de la tabla del modelo definiendo una propiedad table en el modelo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* La tabla asociada con el modelo.
*
* @var string
*/
protected $table = 'my_flights';
}

Claves Primarias

Eloquent también asumirá que cada tabla de base de datos correspondiente al modelo tiene una columna de clave primaria llamada id. Si es necesario, puedes definir una propiedad protegida $primaryKey en tu modelo para especificar una columna diferente que sirva como clave primaria de tu modelo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* La clave primaria asociada con la tabla.
*
* @var string
*/
protected $primaryKey = 'flight_id';
}

Además, Eloquent asume que la clave primaria es un valor entero que se incrementa, lo que significa que Eloquent automáticamente convertirá la clave primaria a un entero. Si deseas utilizar una clave primaria que no sea incremental o que no sea numérica, debes definir una propiedad pública $incrementing en tu modelo que se establezca en false:

<?php
 
class Flight extends Model
{
/**
* Indica si la ID del modelo es de incremento automático.
*
* @var bool
*/
public $incrementing = false;
}

Si la clave primaria de tu modelo no es un entero, debes definir una propiedad protegida $keyType en tu modelo. Esta propiedad debe tener un valor de string:

<?php
 
class Flight extends Model
{
/**
* El tipo de datos de la ID de incremento automático.
*
* @var string
*/
protected $keyType = 'string';
}

Claves Primarias "Compuestas"

Eloquent requiere que cada modelo tenga al menos un "ID" que lo identifique de manera única y que sirva como su clave primaria. Las claves primarias "compuestas" no son compatibles con los modelos Eloquent. Sin embargo, puedes agregar índices únicos compuestos adicionales a tus tablas de base de datos además de la clave primaria única de la tabla.

Claves UUID y ULID

En lugar de usar enteros autoincrementables como las claves primarias de tu modelo Eloquent, puedes optar por usar UUID en su lugar. Los UUID son identificadores alfanuméricos universalmente únicos que tienen 36 caracteres de longitud.

Si deseas que un modelo use una clave UUID en lugar de una clave entera autoincrementable, puedes usar el rasgo Illuminate\Database\Eloquent\Concerns\HasUuids en el modelo. Por supuesto, debes asegurarte de que el modelo tenga una columna de clave primaria equivalente a UUID:

use Illuminate\Database\Eloquent\Concerns\HasUuids;
use Illuminate\Database\Eloquent\Model;
 
class Article extends Model
{
use HasUuids;
 
// ...
}
 
$article = Article::create(['title' => 'Traveling to Europe']);
 
$article->id; // "8f8e8478-9035-4d23-b9a7-62f4d2612ce5"

Por defecto, el rasgo HasUuids generará UUID "ordenados" para tus modelos. Estos UUID son más eficientes para el almacenamiento indexado en la base de datos porque se pueden ordenar lexicográficamente.

Puedes anular el proceso de generación de UUID para un modelo dado al definir un método newUniqueId en el modelo. Además, puedes especificar qué columnas deben recibir UUID al definir un método uniqueIds en el modelo:

use Ramsey\Uuid\Uuid;
 
/**
* Genera un nuevo UUID para el modelo.
*/
public function newUniqueId(): string
{
return (string) Uuid::uuid4();
}
 
/**
* Obtiene las columnas que deberían recibir un identificador único.
*
* @return array<int, string>
*/
public function uniqueIds(): array
{
return ['id', 'discount_code'];
}

Si lo deseas, puedes optar por utilizar "ULID" en lugar de UUID. Los ULID son similares a los UUID; sin embargo, solo tienen 26 caracteres de longitud. Al igual que los UUID ordenados, los ULID son ordenables lexicográficamente para una indexación eficiente en la base de datos. Para utilizar ULID, debes usar el rasgo Illuminate\Database\Eloquent\Concerns\HasUlids en tu modelo. También debes asegurarte de que el modelo tenga una columna de clave primaria equivalente a ULID:

use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Model;
 
class Article extends Model
{
use HasUlids;
 
// ...
}
 
$article = Article::create(['title' => 'Traveling to Asia']);
 
$article->id; // "01gd4d3tgrrfqeda94gdbtdk5c"

Marcas de Tiempo

Por defecto, Eloquent espera que existan las columnas created_at y updated_at en la tabla de base de datos correspondiente a tu modelo. Eloquent establecerá automáticamente los valores de estas columnas cuando se creen o actualicen modelos. Si no deseas que estas columnas sean gestionadas automáticamente por Eloquent, debes definir una propiedad $timestamps en tu modelo con un valor de false:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* Indica si el modelo debe tener marcas de tiempo.
*
* @var bool
*/
public $timestamps = false;
}

Si necesitas personalizar el formato de las marcas de tiempo de tu modelo, establece la propiedad $dateFormat en tu modelo. Esta propiedad determina cómo se almacenan los atributos de fecha en la base de datos, así como su formato cuando el modelo se serializa a una matriz o JSON:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* El formato de almacenamiento de las columnas de fecha del modelo.
*
* @var string
*/
protected $dateFormat = 'U';
}

Si necesitas personalizar los nombres de las columnas utilizadas para almacenar las marcas de tiempo, puedes definir las constantes CREATED_AT y UPDATED_AT en tu modelo:

<?php
 
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}

Si deseas realizar operaciones de modelo sin que el modelo tenga su marca de tiempo updated_at modificada, puedes operar en el modelo dentro de una closure proporcionada al método withoutTimestamps:

Model::withoutTimestamps(fn () => $post->increment(['reads']));

Conexiones de Base de Datos

Por defecto, todos los modelos Eloquent utilizarán la conexión de base de datos predeterminada configurada para tu aplicación. Si deseas especificar una conexión diferente que se debe usar al interactuar con un modelo en particular, debes definir una propiedad $connection en el modelo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* La conexión de base de datos que debe ser utilizada por el modelo.
*
* @var string
*/
protected $connection = 'sqlite';
}

Valores Predeterminados de Atributos

Por defecto, una instancia de modelo recién creada no contendrá valores de atributos. Si deseas definir los valores predeterminados para algunos de los atributos de tu modelo, puedes definir una propiedad $attributes en tu modelo. Los valores de atributos colocados en la matriz $attributes deben estar en su formato crudo y "almacenable", como si se hubieran leído directamente de la base de datos:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* Los valores predeterminados del modelo para atributos.
*
* @var array
*/
protected $attributes = [
'options' => '[]',
'delayed' => false,
];
}

Configuración de Rigidez Eloquent

Laravel ofrece varios métodos que te permiten configurar el comportamiento y la "rigidez" de Eloquent en diversas situaciones.

En primer lugar, el método preventLazyLoading acepta un argumento booleano opcional que indica si se debe evitar la carga diferida. Por ejemplo, es posible que desees desactivar la carga diferida solo en entornos no productivos para que tu entorno de producción continúe funcionando normalmente incluso si hay accidentalmente una relación cargada de forma diferida en el código de producción. Por lo general, este método debería invocarse en el método boot del proveedor de servicios de tu aplicación AppServiceProvider:

use Illuminate\Database\Eloquent\Model;
 
/**
* Inicializa cualquier servicio de la aplicación.
*/
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}

También puedes instruir a Laravel que lance una excepción al intentar completar un atributo que no se puede completar invocando el método preventSilentlyDiscardingAttributes. Esto puede ayudar a prevenir errores inesperados durante el desarrollo local al intentar establecer un atributo que no se ha agregado al array fillable del modelo:

Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction());

Recuperación de Modelos

Una vez que hayas creado un modelo y su tabla de base de datos asociada, estás listo para comenzar a recuperar datos de tu base de datos. Puedes pensar en cada modelo Eloquent como un constructor de consultas potente que te permite consultar de forma fluida la tabla de base de datos asociada con el modelo. El método all del modelo recuperará todos los registros de la tabla de base de datos asociada al modelo:

use App\Models\Flight;
 
foreach (Flight::all() as $flight) {
echo $flight->name;
}

Construcción de Consultas

El método all de Eloquent devolverá todos los resultados en la tabla del modelo. Sin embargo, dado que cada modelo Eloquent sirve como un constructor de consultas, puedes agregar restricciones adicionales a las consultas y luego invocar el método get para recuperar los resultados:

$flights = Flight::where('active', 1)
->orderBy('name')
->take(10)
->get();

Nota Dado que los modelos Eloquent son generadores de consultas, deberías revisar todos los métodos proporcionados por el generador de consultas de Laravel. Puedes usar cualquiera de estos métodos al escribir tus consultas Eloquent.

Actualización de Modelos

Si ya tienes una instancia de un modelo Eloquent que se recuperó de la base de datos, puedes "actualizar" el modelo utilizando los métodos fresh y refresh. El método fresh recuperará nuevamente el modelo de la base de datos. La instancia existente del modelo no se verá afectada:

$flight = Flight::where('number', 'FR 900')->first();
 
$freshFlight = $flight->fresh();

El método refresh volverá a hidratar el modelo existente con datos frescos de la base de datos. Además, todas sus relaciones cargadas se actualizarán también:

$flight = Flight::where('number', 'FR 900')->first();
 
$flight->number = 'FR 456';
 
$flight->refresh();
 
$flight->number; // "FR 900"

Colecciones

Como hemos visto, los métodos Eloquent como all y get recuperan varios registros de la base de datos. Sin embargo, estos métodos no devuelven un array PHP plano. En su lugar, se devuelve una instancia de Illuminate\Database\Eloquent\Collection.

La clase Collection de Eloquent extiende la clase base Illuminate\Support\Collection de Laravel, que proporciona una variedad de métodos útiles para interactuar con colecciones de datos. Por ejemplo, el método reject se puede utilizar para eliminar modelos de una colección según los resultados de una closure invocada:

$flights = Flight::where('destination', 'Paris')->get();
 
$flights = $flights->reject(function (Flight $flight) {
return $flight->cancelled;
});

Además de los métodos proporcionados por la clase base de colecciones de Laravel, la clase de colecciones Eloquent proporciona algunos métodos adicionales que están específicamente destinados a interactuar con colecciones de modelos Eloquent.

Dado que todas las colecciones de Laravel implementan las interfaces iterables de PHP, puedes recorrer las colecciones como si fueran un array:

foreach ($flights as $flight) {
echo $flight->name;
}

División de Resultados

Tu aplicación puede quedarse sin memoria si intentas cargar decenas de miles de registros Eloquent mediante los métodos all o get. En lugar de usar estos métodos, el método chunk se puede utilizar para procesar grandes cantidades de modelos de manera más eficiente.

El método chunk recuperará un subconjunto de modelos Eloquent, pasándolos a una closure para su procesamiento. Dado que solo se recupera el conjunto actual de modelos Eloquent a la vez, el método chunk proporcionará un uso significativamente reducido de la memoria al trabajar con un gran número de modelos:

use App\Models\Flight;
use Illuminate\Database\Eloquent\Collection;
 
Flight::chunk(200, function (Collection $flights) {
foreach ($flights as $flight) {
// ...
}
});

El primer argumento pasado al método chunk es la cantidad de registros que deseas recibir por "trozo". La closure pasada como segundo argumento se invocará para cada trozo que se recupere de la base de datos. Se ejecutará una consulta de base de datos para recuperar cada trozo de registros que se pasa a la closure.

Si estás filtrando los resultados del método chunk según una columna que también actualizarás mientras recorres los resultados, debes usar el método chunkById. Usar el método chunk en estos escenarios podría llevar a resultados inesperados e inconsistentes. Internamente, el método chunkById siempre recuperará modelos con una columna id mayor que el último modelo en el trozo anterior:

Flight::where('departed', true)
->chunkById(200, function (Collection $flights) {
$flights->each->update(['departed' => false]);
}, $column = 'id');

División Utilizando Colecciones Perezosas

El método lazy funciona de manera similar al método chunk en el sentido de que, detrás de escena, ejecuta la consulta en trozos. Sin embargo, en lugar de pasar cada trozo directamente a una callback tal cual, el método lazy devuelve una LazyCollection aplanada de modelos Eloquent, lo que te permite interactuar con los resultados como un solo flujo:

use App\Models\Flight;
 
foreach (Flight::lazy() as $flight) {
// ...
}

Si estás filtrando los resultados del método lazy según una columna que también actualizarás mientras recorres los resultados, debes usar el método lazyById. Internamente, el método lazyById siempre recuperará modelos con una columna id mayor que el último modelo en el trozo anterior:

Flight::where('departed', true)
->lazyById(200, $column = 'id')
->each->update(['departed' => false]);

Puedes filtrar los resultados en función del orden descendente de la columna id utilizando el método lazyByIdDesc.

Cursores

Similar al método lazy, el método cursor se puede utilizar para reducir significativamente el consumo de memoria de tu aplicación al iterar a través de decenas de miles de registros de modelos Eloquent.

El método cursor solo ejecutará una única consulta de base de datos; sin embargo, los modelos Eloquent individuales no se hidratarán hasta que realmente se iteren. Por lo tanto, solo se conserva un modelo Eloquent en memoria en cualquier momento al iterar sobre el cursor.

Advertencia Dado que el método cursor solo mantiene un modelo Eloquent en memoria a la vez, no puede cargar relaciones ansiosamente. Si necesitas cargar relaciones ansiosamente, considera usar el método lazy en su lugar.

Internamente, el método cursor utiliza generadores de PHP para implementar esta funcionalidad:

use App\Models\Flight;
 
foreach (Flight::where('destination', 'Zurich')->cursor() as $flight) {
// ...
}

El método cursor devuelve una instancia de Illuminate\Support\LazyCollection. Las colecciones perezosas (lazy collections) te permiten utilizar muchos de los métodos de colección disponibles en las colecciones típicas de Laravel mientras solo cargas un solo modelo en la memoria a la vez:

use App\Models\User;
 
$users = User::cursor()->filter(function (User $user) {
return $user->id > 500;
});
 
foreach ($users as $user) {
echo $user->id;
}

Aunque el método cursor utiliza mucha menos memoria que una consulta regular (al mantener solo un modelo Eloquent en la memoria a la vez), eventualmente se quedará sin memoria. Esto se debe a que el controlador PDO de PHP internamente almacena en caché todos los resultados brutos de la consulta en su búfer. Si estás tratando con un número muy grande de registros Eloquent, considera usar el método lazy en su lugar.

Subconsultas Avanzadas

Selecciones de Subconsultas

Eloquent también ofrece soporte avanzado para subconsultas, lo que te permite extraer información de tablas relacionadas en una sola consulta. Por ejemplo, imaginemos que tenemos una tabla de "destinos" de vuelo y una tabla de "vuelos" a esos destinos. La tabla "vuelos" contiene una columna "arrived_at" que indica cuándo llegó el vuelo al destino.

Utilizando la funcionalidad de subconsulta disponible para los métodos select y addSelect del constructor de consultas, podemos seleccionar todos los "destinos" y el nombre del vuelo que llegó más recientemente a ese destino con una sola consulta:

use App\Models\Destination;
use App\Models\Flight;
 
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
])->get();

Ordenamiento de Subconsultas

Además, la función orderBy del constructor de consultas admite subconsultas. Continuando con nuestro ejemplo de vuelo, podemos usar esta funcionalidad para ordenar todos los destinos según cuándo llegó el último vuelo a ese destino. Nuevamente, esto se puede hacer mientras se ejecuta una sola consulta de base de datos:

return Destination::orderByDesc(
Flight::select('arrived_at')
->whereColumn('destination_id', 'destinations.id')
->orderByDesc('arrived_at')
->limit(1)
)->get();

Recuperación de Modelos Únicos / Agregados

Además de recuperar todos los registros que coinciden con una consulta dada, también puedes recuperar registros individuales utilizando los métodos find, first o firstWhere. En lugar de devolver una colección de modelos, estos métodos devuelven una sola instancia de modelo:

use App\Models\Flight;
 
// Recupera un modelo por su clave primaria...
$flight = Flight::find(1);
 
// Recupera el primer modelo que coincida con las restricciones de la consulta...
$flight = Flight::where('active', 1)->first();
 
// Alternativa para recuperar el primer modelo que coincida con las restricciones de la consulta...
$flight = Flight::firstWhere('active', 1);

A veces, es posible que desees realizar alguna otra acción si no se encuentran resultados. Los métodos findOr y firstOr devolverán una sola instancia de modelo o, si no se encuentran resultados, ejecutarán la clausura dada. El valor devuelto por la clausura se considerará el resultado del método:

$flight = Flight::findOr(1, function () {
// ...
});
 
$flight = Flight::where('legs', '>', 3)->firstOr(function () {
// ...
});

Excepciones No Encontradas

A veces, es posible que desees lanzar una excepción si no se encuentra un modelo. Esto es particularmente útil en rutas o controladores. Los métodos findOrFail y firstOrFail recuperarán el primer resultado de la consulta; sin embargo, si no se encuentra ningún resultado, se lanzará una excepción Illuminate\Database\Eloquent\ModelNotFoundException:

$flight = Flight::findOrFail(1);
 
$flight = Flight::where('legs', '>', 3)->firstOrFail();

Si no se captura la excepción ModelNotFoundException, se enviará automáticamente una respuesta HTTP 404 al cliente:

use App\Models\Flight;
 
Route::get('/api/flights/{id}', function (string $id) {
return Flight::findOrFail($id);
});

Recuperación o Creación de Modelos

El método firstOrCreate intentará localizar un registro de base de datos utilizando los pares columna/valor dados. Si el modelo no se encuentra en la base de datos, se insertará un registro con los atributos resultantes de fusionar el primer argumento de array con el segundo argumento de array opcional:

El método firstOrNew, al igual que firstOrCreate, intentará localizar un registro en la base de datos que coincida con los atributos dados. Sin embargo, si no se encuentra un modelo, se devolverá una nueva instancia de modelo. Ten en cuenta que el modelo devuelto por firstOrNew aún no se ha guardado en la base de datos. Deberás llamar manualmente al método save para guardarlo:

use App\Models\Flight;
 
// Recupera el vuelo por nombre o créalo si no existe...
$flight = Flight::firstOrCreate([
'name' => 'London to Paris'
]);
 
// Recupera el vuelo por nombre o créalo con los atributos de nombre, demorado y hora de llegada...
$flight = Flight::firstOrCreate(
['name' => 'London to Paris'],
['delayed' => 1, 'arrival_time' => '11:30']
);
 
// Recupera el vuelo por nombre o instancia una nueva instancia de Flight...
$flight = Flight::firstOrNew([
'name' => 'London to Paris'
]);
 
// Recupera el vuelo por nombre o instancia con los atributos de nombre, demorado y hora de llegada...
$flight = Flight::firstOrNew(
['name' => 'Tokyo to Sydney'],
['delayed' => 1, 'arrival_time' => '11:30']
);

Recuperación de Agregados

Al interactuar con modelos Eloquent, también puedes utilizar los métodos count, sum, max y otros métodos de agregación proporcionados por el constructor de consultas de Laravel. Como podrías esperar, estos métodos devuelven un valor escalar en lugar de una instancia de modelo Eloquent:

$count = Flight::where('active', 1)->count();
 
$max = Flight::where('active', 1)->max('price');

Inserción y Actualización de Modelos

Inserciones

Por supuesto, al usar Eloquent, no solo necesitamos recuperar modelos de la base de datos. También necesitamos insertar nuevos registros. Afortunadamente, Eloquent lo hace simple. Para insertar un nuevo registro en la base de datos, debes instanciar una nueva instancia de modelo y establecer atributos en el modelo. Luego, llama al método save en la instancia del modelo:

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Models\Flight;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class FlightController extends Controller
{
/**
* Almacena un nuevo vuelo en la base de datos.
*/
public function store(Request $request): RedirectResponse
{
// Valida la solicitud...
 
$flight = new Flight;
 
$flight->name = $request->name;
 
$flight->save();
 
return redirect('/flights');
}
}

En este ejemplo, asignamos el campo name de la solicitud HTTP entrante al atributo name de la instancia del modelo App\Models\Flight. Cuando llamamos al método save, se insertará un registro en la base de datos. Los sellos de tiempo created_at y updated_at del modelo se establecerán automáticamente cuando se llame al método save, por lo que no es necesario establecerlos manualmente.

Alternativamente, puedes usar el método create para "guardar" un nuevo modelo con una sola instrucción PHP. La instancia del modelo insertado te será devuelta por el método create:

use App\Models\Flight;
 
$flight = Flight::create([
'name' => 'London to Paris',
]);

Sin embargo, antes de usar el método create, deberás especificar una propiedad fillable o guarded en la clase de tu modelo. Estas propiedades son necesarias porque todos los modelos Eloquent están protegidos contra vulnerabilidades de asignación masiva de forma predeterminada. Para obtener más información sobre la asignación masiva, consulta la documentación de asignación masiva.

Actualizaciones

El método save también se puede utilizar para actualizar modelos que ya existen en la base de datos. Para actualizar un modelo, debes recuperarlo y establecer los atributos que deseas actualizar. Luego, debes llamar al método save del modelo. Nuevamente, el sello de tiempo updated_at se actualizará automáticamente, por lo que no es necesario establecer manualmente su valor:

use App\Models\Flight;
 
$flight = Flight::find(1);
 
$flight->name = 'Paris to London';
 
$flight->save();

Actualizaciones Masivas

También se pueden realizar actualizaciones contra modelos que coincidan con una consulta dada. En este ejemplo, todos los vuelos que estén "activos" y tengan un "destino" de "San Diego" se marcarán como retrasados:

Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);

El método update espera un array de pares de columna y valor que representan las columnas que se deben actualizar. El método update devuelve la cantidad de filas afectadas.

Advertencia Al emitir una actualización masiva a través de Eloquent, los eventos de modelo saving, saved, updating y updated no se activarán para los modelos actualizados. Esto se debe a que los modelos nunca se recuperan realmente al emitir una actualización masiva.

Examen de Cambios de Atributos

Eloquent proporciona los métodos isDirty, isClean y wasChanged para examinar el estado interno de tu modelo y determinar cómo han cambiado sus atributos desde que se recuperó originalmente el modelo.

El método isDirty determina si alguno de los atributos del modelo ha cambiado desde que se recuperó el modelo. Puedes pasar un nombre de atributo específico o un array de atributos al método isDirty para determinar si alguno de los atributos está "sucio". El método isClean determinará si un atributo ha permanecido sin cambios desde que se recuperó el modelo. Este método también acepta un argumento de atributo opcional:

use App\Models\User;
 
$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
 
$user->title = 'Painter';
 
$user->isDirty(); // true
$user->isDirty('title'); // true
$user->isDirty('first_name'); // false
$user->isDirty(['first_name', 'title']); // true
 
$user->isClean(); // false
$user->isClean('title'); // false
$user->isClean('first_name'); // true
$user->isClean(['first_name', 'title']); // false
 
$user->save();
 
$user->isDirty(); // false
$user->isClean(); // true

El método wasChanged determina si se cambiaron algunos atributos cuando se guardó el modelo por última vez dentro del ciclo de solicitud actual. Si es necesario, puedes pasar un nombre de atributo para ver si un atributo específico fue cambiado:

$user = User::create([
'first_name' => 'Taylor',
'last_name' => 'Otwell',
'title' => 'Developer',
]);
 
$user->title = 'Painter';
 
$user->save();
 
$user->wasChanged(); // true
$user->wasChanged('title'); // true
$user->wasChanged(['title', 'slug']); // true
$user->wasChanged('first_name'); // false
$user->wasChanged(['first_name', 'title']); // true

El método getOriginal devuelve un array que contiene los atributos originales del modelo independientemente de los cambios en el modelo desde que se recuperó. Si es necesario, puedes pasar un nombre de atributo específico para obtener el valor original de un atributo en particular:

$user = User::find(1);
 
$user->name; // John
$user->email; // [email protected]
 
$user->name = "Jack";
$user->name; // Jack
 
$user->getOriginal('name'); // John
$user->getOriginal(); // Array of original attributes...

Asignación Masiva

Puedes usar el método create para "guardar" un nuevo modelo con una sola instrucción PHP. La instancia del modelo insertado te será devuelta por el método:

use App\Models\Flight;
 
$flight = Flight::create([
'name' => 'London to Paris',
]);

Sin embargo, antes de usar el método create, deberás especificar una propiedad fillable o guarded en la clase de tu modelo. Estas propiedades son necesarias porque todos los modelos Eloquent están protegidos contra vulnerabilidades de asignación masiva de forma predeterminada.

Una vulnerabilidad de asignación masiva ocurre cuando un usuario pasa un campo de solicitud HTTP inesperado y ese campo cambia una columna en tu base de datos que no esperabas. Por ejemplo, un usuario malintencionado podría enviar un parámetro is_admin a través de una solicitud HTTP, que luego se pasa al método create de tu modelo, permitiendo al usuario ascender a administrador.

Entonces, para empezar, debes definir qué atributos del modelo quieres hacer masivamente asignables. Puedes hacer esto usando la propiedad $fillable en el modelo. Por ejemplo, hagamos que el atributo name de nuestro modelo Flight sea masivamente asignable:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class Flight extends Model
{
/**
* Los atributos que se pueden asignar masivamente.
*
* @var array
*/
protected $fillable = ['name'];
}

Una vez que hayas especificado qué atributos son masivamente asignables, puedes usar el método create para insertar un nuevo registro en la base de datos. El método create devuelve la instancia del modelo recién creada:

$flight = Flight::create(['name' => 'London to Paris']);

Si ya tienes una instancia del modelo, puedes usar el método fill para llenarla con un array de atributos:

$flight->fill(['name' => 'Amsterdam to Frankfurt']);

Asignación Masiva y Columnas JSON

Al asignar columnas JSON, la clave masivamente asignable de cada columna debe especificarse en el array $fillable de tu modelo. Por motivos de seguridad, Laravel no admite la actualización de atributos JSON anidados al usar la propiedad guarded:

/**
* Los atributos que se pueden asignar masivamente.
*
* @var array
*/
protected $fillable = [
'options->enabled',
];

Permitir Asignación Masiva

Si deseas hacer todos tus atributos masivamente asignables, puedes definir la propiedad $guarded de tu modelo como un array vacío. Si eliges desproteger tu modelo, debes tener especial cuidado de siempre crear manualmente los arrays pasados a los métodos fill, create y update de Eloquent:

/**
* Los atributos que no se pueden asignar masivamente.
*
* @var array
*/
protected $guarded = [];

Excepciones de Asignación Masiva

Por defecto, los atributos que no están incluidos en el array $fillable se descartan silenciosamente al realizar operaciones de asignación masiva. En producción, este es un comportamiento esperado; sin embargo, durante el desarrollo local puede llevar a la confusión sobre por qué los cambios en el modelo no tienen efecto.

Si lo deseas, puedes indicar a Laravel que lance una excepción al intentar llenar un atributo no asignable mediante la invocación del método preventSilentlyDiscardingAttributes. Normalmente, este método debería invocarse dentro del método boot de uno de los proveedores de servicios de tu aplicación:

use Illuminate\Database\Eloquent\Model;
 
/**
* Inicializa cualquier servicio de la aplicación.
*/
public function boot(): void
{
Model::preventSilentlyDiscardingAttributes($this->app->isLocal());
}

Actualizaciones Incrementales

Ocasionalmente, es posible que necesites actualizar un modelo existente o crear un nuevo modelo si no existe un modelo coincidente. Al igual que el método firstOrCreate, el método updateOrCreate persiste el modelo, por lo que no es necesario llamar manualmente al método save.

En el ejemplo a continuación, si existe un vuelo con una ubicación de salida de Oakland y una ubicación de destino de San Diego, se actualizarán las columnas price y discounted. Si no existe tal vuelo, se creará un nuevo vuelo con los atributos resultantes de fusionar el primer array de argumentos con el segundo array de argumentos:

$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);

Si deseas realizar múltiples "upserts" en una sola consulta, debes utilizar el método upsert. El primer argumento del método consiste en los valores a insertar o actualizar, mientras que el segundo argumento lista la(s) columna(s) que identifican de manera única los registros dentro de la tabla asociada. El tercer y último argumento del método es un array de las columnas que se deben actualizar si ya existe un registro coincidente en la base de datos. El método upsert establecerá automáticamente las marcas de tiempo created_at y updated_at si las marcas de tiempo están habilitadas en el modelo:

Flight::upsert([
['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);

Advertencia Todos los sistemas de gestión de bases de datos excepto SQL Server requieren que las columnas en el segundo argumento del método upsert tengan un índice "primary" o "unique". Además, el controlador de base de datos MySQL ignora el segundo argumento del método upsert y siempre utiliza los índices "primary" y "unique" de la tabla para detectar registros existentes.

Eliminación de Modelos

Para eliminar un modelo, puedes llamar al método delete en la instancia del modelo:

use App\Models\Flight;
 
$flight = Flight::find(1);
 
$flight->delete();

Puedes llamar al método truncate para eliminar todos los registros de la base de datos asociados al modelo. La operación de truncate también restablecerá cualquier ID autoincremental en la tabla asociada al modelo:

Flight::truncate();

Eliminación de un Modelo Existente por su Clave Primaria

En el ejemplo anterior, estamos recuperando el modelo de la base de datos antes de llamar al método delete. Sin embargo, si conoces la clave primaria del modelo, puedes eliminar el modelo sin recuperarlo explícitamente llamando al método destroy. Además de aceptar la única clave primaria, el método destroy aceptará múltiples claves primarias, un array de claves primarias o una colección de claves primarias:

Flight::destroy(1);
 
Flight::destroy(1, 2, 3);
 
Flight::destroy([1, 2, 3]);
 
Flight::destroy(collect([1, 2, 3]));

Advertencia El método destroy carga cada modelo individualmente y llama al método delete para que los eventos de modelo deleting y deleted se envíen correctamente para cada modelo.

Eliminación de Modelos Usando Consultas

Por supuesto, puedes construir una consulta Eloquent para eliminar todos los modelos que coincidan con los criterios de tu consulta. En este ejemplo, eliminaremos todos los vuelos que estén marcados como inactivos. Al igual que las actualizaciones masivas, las eliminaciones masivas no enviarán eventos de modelo para los modelos que se eliminen:

$deleted = Flight::where('active', 0)->delete();

Advertencia Al ejecutar una declaración de eliminación masiva a través de Eloquent, los eventos de modelo deleting y deleted no se enviarán para los modelos eliminados. Esto se debe a que los modelos nunca se recuperan realmente al ejecutar la declaración de eliminación.

Eliminación Suave

Además de eliminar realmente registros de tu base de datos, Eloquent también puede "eliminar suavemente" modelos. Cuando los modelos se eliminan suavemente, en realidad no se eliminan de tu base de datos. En su lugar, se establece un atributo deleted_at en el modelo que indica la fecha y hora en que se "eliminó" el modelo. Para habilitar las eliminaciones suaves para un modelo, agrega el rasgo Illuminate\Database\Eloquent\SoftDeletes al modelo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
 
class Flight extends Model
{
use SoftDeletes;
}

Nota El rasgo SoftDeletes convertirá automáticamente el atributo deleted_at en una instancia de DateTime / Carbon por ti.

También debes agregar la columna deleted_at a la tabla de tu base de datos. El generador de esquemas de Laravel /es/docs/10.x/migrations contiene un método auxiliar para crear esta columna:

use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
 
Schema::table('flights', function (Blueprint $table) {
$table->softDeletes();
});
 
Schema::table('flights', function (Blueprint $table) {
$table->dropSoftDeletes();
});

Ahora, cuando llamas al método delete en el modelo, la columna deleted_at se establecerá en la fecha y hora actual. Sin embargo, el registro de la base de datos del modelo se dejará en la tabla. Al consultar un modelo que utiliza eliminaciones suaves, los modelos eliminados suavemente se excluirán automáticamente de todos los resultados de la consulta.

Para determinar si una instancia de modelo dada ha sido eliminada suavemente, puedes usar el método trashed:

if ($flight->trashed()) {
// ...
}

Restauración de Modelos Eliminados Suavemente

A veces, es posible que desees "deseliminar" un modelo eliminado suavemente. Para restaurar un modelo eliminado suavemente, puedes llamar al método restore en una instancia del modelo. El método restore establecerá la columna deleted_at del modelo en null:

$flight->restore();

También puedes usar el método restore en una consulta para restaurar varios modelos. Nuevamente, al igual que con otras operaciones "masivas", esto no enviará eventos de modelo para los modelos que se restauran:

Flight::withTrashed()
->where('airline_id', 1)
->restore();

El método restore también se puede utilizar al construir consultas de relaciones:

$flight->history()->restore();

Eliminación Permanente de Modelos

A veces, es posible que necesites eliminar realmente un modelo de tu base de datos. Puedes utilizar el método forceDelete para eliminar permanentemente un modelo eliminado suavemente de la tabla de la base de datos:

$flight->forceDelete();

También puedes utilizar el método forceDelete al construir consultas de relaciones Eloquent:

$flight->history()->forceDelete();

Consulta de Modelos Eliminados Suavemente

Inclusión de Modelos Eliminados Suavemente

Como se señaló anteriormente, los modelos eliminados suavemente se excluyen automáticamente de los resultados de la consulta. Sin embargo, puedes forzar que los modelos eliminados suavemente se incluyan en los resultados de una consulta llamando al método withTrashed en la consulta:

use App\Models\Flight;
 
$flights = Flight::withTrashed()
->where('account_id', 1)
->get();

El método withTrashed también se puede llamar al construir una consulta de relación:

$flight->history()->withTrashed()->get();

Recuperación Solo de Modelos Eliminados Suavemente

El método onlyTrashed recuperará solo los modelos eliminados suavemente:

$flights = Flight::onlyTrashed()
->where('airline_id', 1)
->get();

Poda de Modelos

A veces, es posible que desees eliminar periódicamente modelos que ya no son necesarios. Para lograr esto, puedes agregar el rasgo Illuminate\Database\Eloquent\Prunable o Illuminate\Database\Eloquent\MassPrunable a los modelos que te gustaría podar periódicamente. Después de agregar uno de los rasgos al modelo, implementa un método prunable que devuelva un generador de consultas Eloquent que resuelva los modelos que ya no son necesarios:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Prunable;
 
class Flight extends Model
{
use Prunable;
 
/**
* Obtén la consulta del modelo eliminable.
*/
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subMonth());
}
}

Cuando marcas modelos como Prunable, también puedes definir un método pruning en el modelo. Este método se llamará antes de que se elimine permanentemente el modelo. Este método puede ser útil para eliminar cualquier recurso adicional asociado con el modelo, como archivos almacenados, antes de que el modelo se elimine permanentemente de la base de datos:

/**
* Prepara el modelo para el borrado.
*/
protected function pruning(): void
{
// ...
}

Después de configurar tu modelo podable, debes programar el comando Artisan model:prune en la clase App\Console\Kernel de tu aplicación. Eres libre de elegir el intervalo apropiado en el que se debe ejecutar este comando:

/**
* Define la programación de comandos de la aplicación.
*/
protected function schedule(Schedule $schedule): void
{
$schedule->command('model:prune')->daily();
}

En segundo plano, el comando model:prune detectará automáticamente los modelos "Prunables" dentro del directorio app/Models de tu aplicación. Si tus modelos están en una ubicación diferente, puedes usar la opción --model para especificar los nombres de las clases de modelo:

$schedule->command('model:prune', [
'--model' => [Address::class, Flight::class],
])->daily();

Si deseas excluir ciertos modelos de ser podados mientras podas todos los demás modelos detectados, puedes usar la opción --except:

$schedule->command('model:prune', [
'--except' => [Address::class, Flight::class],
])->daily();

Puedes probar tu consulta prunable ejecutando el comando model:prune con la opción --pretend. Cuando simulas, el comando model:prune simplemente informará cuántos registros se podarían si el comando se ejecutara realmente:

php artisan model:prune --pretend

Advertencia Los modelos eliminados suavemente serán eliminados permanentemente (forceDelete) si coinciden con la consulta de eliminación suave.

Poda Masiva

Cuando los modelos están marcados con el rasgo Illuminate\Database\Eloquent\MassPrunable, los modelos se eliminan de la base de datos mediante consultas de eliminación masiva. Por lo tanto, el método pruning no se invocará, ni se enviarán los eventos de modelo deleting y deleted. Esto se debe a que los modelos nunca se recuperan realmente antes de la eliminación, haciendo que el proceso de poda sea mucho más eficiente:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\MassPrunable;
 
class Flight extends Model
{
use MassPrunable;
 
/**
* Obtén la consulta del modelo eliminable.
*/
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subMonth());
}
}

Replicación de Modelos

Puedes crear una copia no guardada de una instancia existente de un modelo usando el método replicate. Este método es particularmente útil cuando tienes instancias de modelos que comparten muchas de las mismas atributos:

use App\Models\Address;
 
$shipping = Address::create([
'type' => 'shipping',
'line_1' => '123 Example Street',
'city' => 'Victorville',
'state' => 'CA',
'postcode' => '90001',
]);
 
$billing = $shipping->replicate()->fill([
'type' => 'billing'
]);
 
$billing->save();

Para excluir uno o más atributos de ser replicados al nuevo modelo, puedes pasar un array al método replicate:

$flight = Flight::create([
'destination' => 'LAX',
'origin' => 'LHR',
'last_flown' => '2020-03-04 11:00:00',
'last_pilot_id' => 747,
]);
 
$flight = $flight->replicate([
'last_flown',
'last_pilot_id'
]);

Ámbitos de Consulta

Ámbitos Globales

Los ámbitos globales te permiten agregar restricciones a todas las consultas para un modelo dado. La funcionalidad de eliminación suave de Laravel utiliza ámbitos globales para recuperar solo modelos "no eliminados" de la base de datos. Escribir tus propios ámbitos globales puede proporcionar una forma conveniente y fácil de asegurarte de que cada consulta para un modelo dado reciba ciertas restricciones.

Generación de Ámbitos

Para generar un nuevo ámbito global, puedes invocar el comando Artisan make:scope, que colocará el ámbito generado en el directorio app/Models/Scopes de tu aplicación:

php artisan make:scope AncientScope

Escritura de Ámbitos Globales

Escribir un ámbito global es simple. Primero, usa el comando make:scope para generar una clase que implemente la interfaz Illuminate\Database\Eloquent\Scope. La interfaz Scope requiere que implementes un método: apply. El método apply puede agregar restricciones where u otros tipos de cláusulas a la consulta según sea necesario:

<?php
 
namespace App\Models\Scopes;
 
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Scope;
 
class AncientScope implements Scope
{
/**
* Aplica el ámbito a un generador de consultas Eloquent dado.
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where('created_at', '<', now()->subYears(2000));
}
}

Nota Si tu ámbito global está agregando columnas a la cláusula select de la consulta, deberías usar el método addSelect en lugar de select. Esto evitará la sustitución involuntaria de la cláusula select existente de la consulta.

Aplicación de Ámbitos Globales

Para asignar un ámbito global a un modelo, debes anular el método booted del modelo e invocar el método addGlobalScope del modelo. El método addGlobalScope acepta una instancia de tu ámbito como su único argumento:

<?php
 
namespace App\Models;
 
use App\Models\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* El método "booted" del modelo.
*/
protected static function booted(): void
{
static::addGlobalScope(new AncientScope);
}
}

Después de agregar el ámbito en el ejemplo anterior al modelo App\Models\User, una llamada al método User::all() ejecutará la siguiente consulta SQL:

select * from `users` where `created_at` < 0021-02-18 00:00:00

Ámbitos Globales Anónimos

Eloquent también te permite definir ámbitos globales usando closures, lo cual es particularmente útil para ámbitos simples que no justifican una clase separada. Al definir un ámbito global usando una closure, debes proporcionar un nombre de ámbito de tu elección como primer argumento al método addGlobalScope:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* El método "booted" del modelo.
*/
protected static function booted(): void
{
static::addGlobalScope('ancient', function (Builder $builder) {
$builder->where('created_at', '<', now()->subYears(2000));
});
}
}

Eliminación de Ámbitos Globales

Si deseas eliminar un ámbito global para una consulta dada, puedes usar el método withoutGlobalScope. Este método acepta el nombre de la clase del ámbito global como su único argumento:

User::withoutGlobalScope(AncientScope::class)->get();

O, si definiste el ámbito global usando una closure, debes pasar el nombre de cadena que asignaste al ámbito global:

User::withoutGlobalScope('ancient')->get();

Si deseas eliminar varios o incluso todos los ámbitos globales de la consulta, puedes usar el método withoutGlobalScopes:

// Elimina todos los ámbitos globales...
User::withoutGlobalScopes()->get();
 
// Elimina algunos de los ámbitos globales...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();

Ámbitos Locales

Los ámbitos locales te permiten definir conjuntos comunes de restricciones de consulta que puedes reutilizar fácilmente en toda tu aplicación. Por ejemplo, es posible que debas recuperar con frecuencia todos los usuarios que se consideran "populares". Para definir un ámbito, agrega un método del modelo Eloquent con el prefijo scope.

Los ámbitos siempre deben devolver la misma instancia del generador de consultas o void:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* Limita una consulta para incluir solo usuarios populares.
*/
public function scopePopular(Builder $query): void
{
$query->where('votes', '>', 100);
}
 
/**
* Limita una consulta para incluir solo usuarios activos.
*/
public function scopeActive(Builder $query): void
{
$query->where('active', 1);
}
}

Utilización de un Ámbito Local

Una vez que se haya definido el ámbito, puedes llamar a los métodos de ámbito al consultar el modelo. Sin embargo, no debes incluir el prefijo scope al llamar al método. Incluso puedes encadenar llamadas a varios ámbitos:

use App\Models\User;
 
$users = User::popular()->active()->orderBy('created_at')->get();

Combinar múltiples ámbitos de modelo Eloquent mediante un operador de consulta or puede requerir el uso de closures para lograr el agrupamiento lógico correcto:

$users = User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();

Sin embargo, como esto puede ser engorroso, Laravel proporciona un método orWhere de "orden superior" que te permite encadenar fluidamente ámbitos sin el uso de closures:

$users = User::popular()->orWhere->active()->get();

Ámbitos Dinámicos

A veces, es posible que desees definir un ámbito que acepte parámetros. Para comenzar, solo agrega tus parámetros adicionales a la firma del método de ámbito.

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* Limita una consulta para incluir solo usuarios de un tipo dado.
*/
public function scopeOfType(Builder $query, string $type): void
{
$query->where('type', $type);
}
}

Una vez que se hayan agregado los argumentos esperados a la firma del método de ámbito, puedes pasar los argumentos al llamar al ámbito:

$users = User::ofType('admin')->get();

Comparación de Modelos

A veces, es posible que necesites determinar si dos modelos son "iguales" o no. Los métodos is y isNot se pueden usar para verificar rápidamente si dos modelos tienen la misma clave principal, tabla y conexión de base de datos o no:

if ($post->is($anotherPost)) {
// ...
}
 
if ($post->isNot($anotherPost)) {
// ...
}

Los métodos is y isNot también están disponibles al usar las relaciones belongsTo, hasOne, morphTo y morphOne. Este método es particularmente útil cuando deseas comparar un modelo relacionado sin emitir una consulta para recuperar ese modelo:

if ($post->author()->is($user)) {
// ...
}

Eventos

Nota ¿Quieres transmitir tus eventos de Eloquent directamente a tu aplicación del lado del cliente? Consulta la transmisión de eventos de modelo de Laravel.

Los modelos Eloquent despachan varios eventos, lo que te permite enganchar en los siguientes momentos del ciclo de vida de un modelo: retrieved, creating, created, updating, updated, saving, saved, deleting, deleted, trashed, forceDeleting, forceDeleted, restoring, restored y replicating.

El evento retrieved se despachará cuando se recupera un modelo existente de la base de datos. Cuando se guarda un nuevo modelo por primera vez, se despacharán los eventos creating y created. Los eventos updating / updated se despacharán cuando se modifica un modelo existente y se llama al método save. Los eventos saving / saved se despacharán cuando se crea o actualiza un modelo, incluso si los atributos del modelo no han cambiado. Los nombres de eventos que terminan con -ing se despachan antes de que se persistan los cambios en el modelo, mientras que los eventos que terminan con -ed se despachan después de que se persisten los cambios en el modelo.

Para comenzar a escuchar eventos de modelos, define una propiedad $dispatchesEvents en tu modelo Eloquent. Esta propiedad asigna varios puntos del ciclo de vida del modelo Eloquent a tus propias clases de eventos. Cada clase de evento de modelo debe esperar recibir una instancia del modelo afectado a través de su constructor:

<?php
 
namespace App\Models;
 
use App\Events\UserDeleted;
use App\Events\UserSaved;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
 
class User extends Authenticatable
{
use Notifiable;
 
/**
* El mapa de eventos para el modelo.
*
* @var array
*/
protected $dispatchesEvents = [
'saved' => UserSaved::class,
'deleted' => UserDeleted::class,
];
}

Después de definir y asignar tus eventos Eloquent, puedes usar escuchadores de eventos para manejar los eventos.

Advertencia Al emitir una consulta de actualización o eliminación masiva a través de Eloquent, los eventos de modelo saved, updated, deleting y deleted no se enviarán para los modelos afectados. Esto se debe a que los modelos nunca se recuperan realmente al realizar actualizaciones o eliminaciones masivas.

Uso de Cierres

En lugar de utilizar clases de eventos personalizadas, puedes registrar closures que se ejecuten cuando se despachan varios eventos de modelo. Por lo general, deberías registrar estas closures en el método booted de tu modelo:

<?php
 
namespace App\Models;
 
use Illuminate\Database\Eloquent\Model;
 
class User extends Model
{
/**
* El método "booted" del modelo.
*/
protected static function booted(): void
{
static::created(function (User $user) {
// ...
});
}
}

Si es necesario, puedes utilizar escuchadores de eventos anónimos en cola al registrar eventos de modelo. Esto instruirá a Laravel para ejecutar el escuchador de eventos de modelo en segundo plano utilizando la cola de tu aplicación:

use function Illuminate\Events\queueable;
 
static::created(queueable(function (User $user) {
// ...
}));

Observadores

Definición de Observadores

Si estás escuchando muchos eventos en un modelo dado, puedes utilizar observadores para agrupar todos tus escuchadores en una sola clase. Las clases de observadores tienen nombres de métodos que reflejan los eventos de Eloquent que deseas escuchar. Cada uno de estos métodos recibe el modelo afectado como su único argumento. El comando Artisan make:observer es la forma más sencilla de crear una nueva clase de observador:

php artisan make:observer UserObserver --model=User

Este comando colocará el nuevo observador en tu directorio app/Observers. Si este directorio no existe, Artisan lo creará por ti. Tu observador recién creado se verá así:

<?php
 
namespace App\Observers;
 
use App\Models\User;
 
class UserObserver
{
/**
* Maneja el evento "created" del usuario.
*/
public function created(User $user): void
{
// ...
}
 
/**
* Maneja el evento "updated" del usuario.
*/
public function updated(User $user): void
{
// ...
}
 
/**
* Maneja el evento "deleted" del usuario.
*/
public function deleted(User $user): void
{
// ...
}
 
/**
* Maneja el evento "restored" del usuario.
*/
public function restored(User $user): void
{
// ...
}
 
/**
* Maneja el evento "forceDeleted" del usuario.
*/
public function forceDeleted(User $user): void
{
// ...
}
}

Para registrar un observador, debes llamar al método observe en el modelo que deseas observar. Puedes registrar observadores en el método boot del proveedor de servicios App\Providers\EventServiceProvider de tu aplicación:

use App\Models\User;
use App\Observers\UserObserver;
 
/**
* Registra cualquier evento para tu aplicación.
*/
public function boot(): void
{
User::observe(UserObserver::class);
}

Alternativamente, puedes listar tus observadores dentro de una propiedad $observers de la clase App\Providers\EventServiceProvider de tu aplicación:

use App\Models\User;
use App\Observers\UserObserver;
 
/**
* Los observadores del modelo para tu aplicación.
*
* @var array
*/
protected $observers = [
User::class => [UserObserver::class],
];

Nota Existen eventos adicionales a los que un observador puede escuchar, como saving y retrieved. Estos eventos se describen en la documentación de eventos.

Observadores y Transacciones de Base de Datos

Cuando se están creando modelos dentro de una transacción de base de datos, es posible que desees indicar a un observador que solo ejecute sus manipuladores de eventos después de que se haya confirmado la transacción de base de datos. Puedes lograr esto implementando la interfaz ShouldHandleEventsAfterCommit en tu observador. Si no hay una transacción de base de datos en curso, los manipuladores de eventos se ejecutarán inmediatamente:

<?php
 
namespace App\Observers;
 
use App\Models\User;
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
 
class UserObserver implements ShouldHandleEventsAfterCommit
{
/**
* Maneja el evento "created" del usuario.
*/
public function created(User $user): void
{
// ...
}
}

Silenciamiento de Eventos

Ocasionalmente, es posible que necesites "silenciar" temporalmente todos los eventos generados por un modelo. Puedes lograr esto utilizando el método withoutEvents. El método withoutEvents acepta una closure como su único argumento. Cualquier código ejecutado dentro de esta closure no despachará eventos de modelo, y cualquier valor devuelto por la closure será devuelto por el método withoutEvents:

use App\Models\User;
 
$user = User::withoutEvents(function () {
User::findOrFail(1)->delete();
 
return User::find(2);
});

Guardado de un Solo Modelo sin Eventos

A veces, es posible que desees "guardar" un modelo dado sin despachar eventos. Puedes lograr esto utilizando el método saveQuietly:

$user = User::findOrFail(1);
 
$user->name = 'Victoria Faith';
 
$user->saveQuietly();

También puedes "actualizar", "eliminar", "eliminar suavemente", "restaurar" y "replicar" un modelo dado sin despachar eventos:

$user->deleteQuietly();
$user->forceDeleteQuietly();
$user->restoreQuietly();