- Introducción
- Generación de clases Model
- Convenciones de los modelos Eloquent
- Recuperación de modelos
- Recuperación de modelos individuales / agregados
- Inserción y actualización de modelos
- Eliminación de modelos
- Poda de modelos
- Replicación de modelos
- Scopes de consulta
- Comparación de modelos
- Eventos
#Introducción
Laravel incluye Eloquent, un mapeador objeto-relacional (ORM) que hace que interactuar con su base de datos sea agradable. Al usar Eloquent, cada tabla de base de datos tiene un "Model" correspondiente que se utiliza para interactuar con esa tabla. Además de recuperar registros de la tabla de base de datos, los modelos Eloquent le permiten insertar, actualizar y eliminar registros de la tabla también.
Antes de comenzar, asegúrese de configurar una conexión a la base de datos en el archivo de configuración config/database.php de su aplicación. Para más información sobre cómo configurar su base de datos, consulte la documentación de configuración de base de datos.
#Laravel Bootcamp
Si es nuevo en Laravel, siéntase libre de comenzar con el Laravel Bootcamp. El Laravel Bootcamp le guiará en la construcción de su primera aplicación Laravel usando Eloquent. Es una excelente manera de conocer todo lo que Laravel y Eloquent tienen para ofrecer.
#Generación de clases Model
Para comenzar, vamos a crear un modelo Eloquent. Los modelos normalmente se encuentran en el directorio app\Models y extienden la clase Illuminate\Database\Eloquent\Model. Puede usar el comando make:model de Artisan para generar un nuevo modelo:
php artisan make:model Flight
Si desea generar una migración de base de datos al generar el modelo, puede usar la opción --migration o -m:
php artisan make:model Flight --migration
Puede generar varios otros tipos de clases al generar un modelo, como factories, seeders, policies, controllers y form requests. Además, estas opciones pueden combinarse para crear múltiples clases a la vez:
# Generar un modelo y una clase FlightFactory...
php artisan make:model Flight --factory
php artisan make:model Flight -f
# Generar un modelo y una clase FlightSeeder...
php artisan make:model Flight --seed
php artisan make:model Flight -s
# Generar un modelo y una clase FlightController...
php artisan make:model Flight --controller
php artisan make:model Flight -c
# Generar un modelo, clase resource FlightController y clases de form request...
php artisan make:model Flight --controller --resource --requests
php artisan make:model Flight -crR
# Generar un modelo y una clase FlightPolicy...
php artisan make:model Flight --policy
# Generar un modelo, migración, factory, seeder y controller...
php artisan make:model Flight -mfsc
# Atajo para generar un modelo, migración, factory, seeder, policy, controller y form requests...
php artisan make:model Flight --all
# Generar un modelo pivot...
php artisan make:model Member --pivot
php artisan make:model Member -p
#Inspección de modelos
A veces puede ser difícil determinar todos los atributos y relaciones disponibles de un modelo solo con revisar su código. En su lugar, pruebe el comando Artisan model:show, que proporciona una vista general conveniente de todos los atributos y relaciones del modelo:
php artisan model:show Flight
#Convenciones de los modelos Eloquent
Los modelos generados por el comando make:model se colocarán en el directorio app/Models. Examinemos una clase 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 observar el ejemplo anterior, puede que haya notado que no le indicamos a Eloquent qué tabla de base de datos corresponde a nuestro modelo Flight. Por convención, se usará el nombre plural en "snake case" de la clase como nombre de la tabla, a menos que se especifique otro nombre explícitamente. Así, 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 su modelo no sigue esta convención, puede especificar manualmente el nombre de la tabla 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 la tabla de base de datos correspondiente a cada modelo tiene una columna de clave primaria llamada id. Si es necesario, puede definir una propiedad protegida $primaryKey en su modelo para especificar una columna diferente que sirva como clave primaria del 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 autoincremental, lo que significa que Eloquent convertirá automáticamente la clave primaria a un entero. Si desea usar una clave primaria no autoincremental o no numérica, debe definir una propiedad pública $incrementing en su modelo con valor false:
<?php
class Flight extends Model
{
/**
* Indica si el ID del modelo es autoincremental.
*
* @var bool
*/
public $incrementing = false;
}
Si la clave primaria de su modelo no es un entero, debe definir una propiedad protegida $keyType en su modelo. Esta propiedad debe tener el valor string:
<?php
class Flight extends Model
{
/**
* El tipo de dato del ID de la clave primaria.
*
* @var string
*/
protected $keyType = 'string';
}
#Claves primarias "compuestas"
Eloquent requiere que cada modelo tenga al menos un "ID" único que pueda servir como clave primaria. Las claves primarias "compuestas" no son compatibles con los modelos Eloquent. Sin embargo, puede agregar índices únicos adicionales de múltiples columnas a sus tablas de base de datos además de la clave primaria única de la tabla.
#Claves UUID y ULID
En lugar de usar enteros autoincrementales como claves primarias de su modelo Eloquent, puede optar por usar UUIDs. Los UUIDs son identificadores alfanuméricos universalmente únicos de 36 caracteres de longitud.
Si desea que un modelo use una clave UUID en lugar de una clave entera autoincremental, puede usar el trait Illuminate\Database\Eloquent\Concerns\HasUuids en el modelo. Por supuesto, debe asegurarse 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 trait HasUuids generará UUIDs "ordenados" para sus modelos. Estos UUIDs son más eficientes para el almacenamiento indexado en bases de datos porque pueden ordenarse lexicográficamente.
Puede sobrescribir el proceso de generación de UUID para un modelo dado definiendo un método newUniqueId en el modelo. Además, puede especificar qué columnas deben recibir UUIDs definiendo un método uniqueIds en el modelo:
use Ramsey\Uuid\Uuid;
/**
* Generar un nuevo UUID para el modelo.
*/
public function newUniqueId(): string
{
return (string) Uuid::uuid4();
}
/**
* Obtener las columnas que deben recibir un identificador único.
*
* @return array<int, string>
*/
public function uniqueIds(): array
{
return ['id', 'discount_code'];
}
Si lo desea, puede optar por utilizar "ULIDs" en lugar de UUIDs. Los ULIDs son similares a los UUIDs; sin embargo, tienen solo 26 caracteres de longitud. Al igual que los UUIDs ordenados, los ULIDs son ordenables lexicográficamente para un indexado eficiente en bases de datos. Para utilizar ULIDs, debe usar el trait Illuminate\Database\Eloquent\Concerns\HasUlids en su modelo. También debe asegurarse 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"
#Timestamps
Por defecto, Eloquent espera que existan columnas created_at y updated_at en la tabla de base de datos correspondiente a su modelo. Eloquent establecerá automáticamente los valores de estas columnas cuando los modelos sean creados o actualizados. Si no desea que estas columnas sean gestionadas automáticamente por Eloquent, debe definir una propiedad $timestamps en su modelo con valor false:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Indica si el modelo debe tener timestamps.
*
* @var bool
*/
public $timestamps = false;
}
Si necesita personalizar el formato de los timestamps de su modelo, establezca la propiedad $dateFormat en su 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 un array 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 necesita personalizar los nombres de las columnas usadas para almacenar los timestamps, puede definir las constantes CREATED_AT y UPDATED_AT en su modelo:
<?php
class Flight extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'updated_date';
}
Si desea realizar operaciones sobre el modelo sin que se modifique el timestamp updated_at, puede operar sobre el modelo dentro de un closure pasado al método withoutTimestamps:
Model::withoutTimestamps(fn () => $post->increment(['reads']));
#Conexiones a bases de datos
Por defecto, todos los modelos Eloquent usarán la conexión de base de datos predeterminada configurada para su aplicación. Si desea especificar una conexión diferente que se debe usar al interactuar con un modelo en particular, debe 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 usar 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 desea definir valores predeterminados para algunos atributos de su modelo, puede definir una propiedad $attributes en su modelo. Los valores de atributos colocados en el array $attributes deben estar en su formato crudo, "almenable", como si acabaran de ser leídos de la base de datos:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Los valores predeterminados de atributos del modelo.
*
* @var array
*/
protected $attributes = [
'options' => '[]',
'delayed' => false,
];
}
#Configuración de la estricta de Eloquent
Laravel ofrece varios métodos que le permiten configurar el comportamiento y la "estricta" de Eloquent en diversas situaciones.
Primero, el método preventLazyLoading acepta un argumento booleano opcional que indica si se debe prevenir la carga perezosa. Por ejemplo, puede que desee deshabilitar la carga perezosa solo en entornos que no sean de producción para que su entorno de producción continúe funcionando normalmente incluso si una relación cargada perezosamente está presente accidentalmente en el código de producción. Normalmente, este método debe invocarse en el método boot del AppServiceProvider de su aplicación:
use Illuminate\Database\Eloquent\Model;
/**
* Bootstrap de cualquier servicio de la aplicación.
*/
public function boot(): void
{
Model::preventLazyLoading(! $this->app->isProduction());
}
Además, puede indicar a Laravel que lance una excepción al intentar llenar un atributo no asignable invocando el método preventSilentlyDiscardingAttributes. Esto puede ayudar a prevenir errores inesperados durante el desarrollo local al intentar establecer un atributo que no ha sido agregado al array fillable del modelo:
Model::preventSilentlyDiscardingAttributes(! $this->app->isProduction());
#Recuperación de modelos
Una vez que haya creado un modelo y su tabla de base de datos asociada, estará listo para comenzar a recuperar datos de su base de datos. Puede pensar en cada modelo Eloquent como un poderoso query builder que le permite consultar fluidamente la tabla de base de datos asociada al modelo. El método all del modelo recuperará todos los registros de la tabla 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 de la tabla del modelo. Sin embargo, dado que cada modelo Eloquent funciona como un query builder, puede 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();
Dado que los modelos Eloquent son query builders, debería revisar todos los métodos proporcionados por el query builder de Laravel. Puede usar cualquiera de estos métodos al escribir sus consultas Eloquent.
#Actualización de modelos
Si ya tiene una instancia de un modelo Eloquent que fue recuperada de la base de datos, puede "refrescar" el modelo usando los métodos fresh y refresh. El método fresh volverá a recuperar 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 la instancia existente del modelo usando datos frescos de la base de datos. Además, todas sus relaciones cargadas también serán refrescadas:
$flight = Flight::where('number', 'FR 900')->first();
$flight->number = 'FR 456';
$flight->refresh();
$flight->number; // "FR 900"
#Colecciones
Como hemos visto, métodos de Eloquent como all y get recuperan múltiples registros de la base de datos. Sin embargo, estos métodos no devuelven un array PHP simple. En su lugar, devuelven 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 puede usarse para eliminar modelos de una colección basándose en los resultados de un closure invocado:
$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 de Eloquent ofrece 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, puede iterar sobre las colecciones como si fueran un array:
foreach ($flights as $flight) {
echo $flight->name;
}
#Procesamiento por bloques
Su aplicación puede quedarse sin memoria si intenta cargar decenas de miles de registros Eloquent mediante los métodos all o get. En lugar de usar estos métodos, puede usar el método chunk para procesar grandes cantidades de modelos de manera más eficiente.
El método chunk recuperará un subconjunto de modelos Eloquent, pasándolos a un closure para su procesamiento. Dado que solo se recupera el bloque actual de modelos Eloquent a la vez, el método chunk proporcionará un uso de memoria significativamente reducido 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 el número de registros que desea recibir por "bloque". El closure pasado como segundo argumento será invocado para cada bloque que se recupere de la base de datos. Se ejecutará una consulta a la base de datos para recuperar cada bloque de registros pasado al closure.
Si está filtrando los resultados del método chunk basándose en una columna que también actualizará mientras itera sobre los resultados, debe 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 del bloque anterior:
Flight::where('departed', true)
->chunkById(200, function (Collection $flights) {
$flights->each->update(['departed' => false]);
}, $column = 'id');
#Procesamiento por bloques usando colecciones Lazy
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 bloques. Sin embargo, en lugar de pasar cada bloque directamente a un callback, el método lazy devuelve una LazyCollection aplanada de modelos Eloquent, lo que le permite interactuar con los resultados como un flujo único:
use App\Models\Flight;
foreach (Flight::lazy() as $flight) {
// ...
}
Si está filtrando los resultados del método lazy basándose en una columna que también actualizará mientras itera sobre los resultados, debe usar el método lazyById. Internamente, el método lazyById siempre recuperará modelos con una columna id mayor que el último modelo del bloque anterior:
Flight::where('departed', true)
->lazyById(200, $column = 'id')
->each->update(['departed' => false]);
Puede filtrar los resultados basándose en el orden descendente de la columna id usando el método lazyByIdDesc.
#Cursores
Similar al método lazy, el método cursor puede usarse para reducir significativamente el consumo de memoria de su aplicación al iterar sobre decenas de miles de registros de modelos Eloquent.
El método cursor solo ejecutará una única consulta a la base de datos; sin embargo, los modelos Eloquent individuales no se hidratarán hasta que realmente se iteren. Por lo tanto, solo un modelo Eloquent se mantiene en memoria en un momento dado mientras se itera sobre el cursor.
Dado que el método cursor solo mantiene un modelo Eloquent en memoria a la vez, no puede cargar relaciones eager. Si necesita cargar relaciones eager, considere usar el método lazy en su lugar.
Internamente, el método cursor usa 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 Lazy le permiten usar muchos de los métodos de colección disponibles en las colecciones típicas de Laravel mientras solo carga un modelo en 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 usa mucha menos memoria que una consulta regular (al mantener solo un modelo Eloquent en memoria a la vez), eventualmente también se quedará sin memoria. Esto se debe a que el driver PDO de PHP almacena en caché internamente todos los resultados de consultas en bruto en su buffer. Si está trabajando con un número muy grande de registros Eloquent, considere usar el método lazy en su lugar.
#Subconsultas avanzadas
#Selecciones con subconsultas
Eloquent también ofrece soporte avanzado para subconsultas, lo que le permite obtener información de tablas relacionadas en una sola consulta. Por ejemplo, imaginemos que tenemos una tabla de destinations de vuelos y una tabla de flights hacia esos destinos. La tabla flights contiene una columna arrived_at que indica cuándo llegó el vuelo al destino.
Usando la funcionalidad de subconsulta disponible en los métodos select y addSelect del query builder, podemos seleccionar todos los destinations y el nombre del vuelo que llegó más recientemente a ese destino usando 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 con subconsultas
Además, la función orderBy del query builder soporta subconsultas. Continuando con nuestro ejemplo de vuelos, podemos usar esta funcionalidad para ordenar todos los destinos basándonos en cuándo llegó el último vuelo a ese destino. Nuevamente, esto puede hacerse ejecutando una sola consulta a la 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 individuales / agregados
Además de recuperar todos los registros que coinciden con una consulta dada, también puede recuperar registros individuales usando los métodos find, first o firstWhere. En lugar de devolver una colección de modelos, estos métodos devuelven una única instancia de modelo:
use App\Models\Flight;
// Recuperar un modelo por su clave primaria...
$flight = Flight::find(1);
// Recuperar 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 puede que desee realizar alguna otra acción si no se encuentran resultados. Los métodos findOr y firstOr devolverán una instancia de modelo o, si no se encuentran resultados, ejecutarán el closure dado. El valor devuelto por el closure será considerado el resultado del método:
$flight = Flight::findOr(1, function () {
// ...
});
$flight = Flight::where('legs', '>', 3)->firstOr(function () {
// ...
});
#Excepciones de no encontrado
A veces puede que desee lanzar una excepción si un modelo no se encuentra. Esto es especialmente ú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 la excepción ModelNotFoundException no es capturada, 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);
});
#Recuperar o Crear Modelos
El método firstOrCreate intentará localizar un registro en la base de datos usando 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 combinar el primer argumento array con el segundo argumento 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 del modelo. Tenga en cuenta que el modelo devuelto por firstOrNew aún no ha sido persistido en la base de datos. Deberá llamar manualmente al método save para persistirlo:
use App\Models\Flight;
// Recuperar vuelo por nombre o crearlo si no existe...
$flight = Flight::firstOrCreate([
'name' => 'London to Paris'
]);
// Recuperar vuelo por nombre o crearlo con los atributos name, delayed y arrival_time...
$flight = Flight::firstOrCreate(
['name' => 'London to Paris'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Recuperar vuelo por nombre o instanciar una nueva instancia de Flight...
$flight = Flight::firstOrNew([
'name' => 'London to Paris'
]);
// Recuperar vuelo por nombre o instanciar con los atributos name, delayed y arrival_time...
$flight = Flight::firstOrNew(
['name' => 'Tokyo to Sydney'],
['delayed' => 1, 'arrival_time' => '11:30']
);
#Recuperar Agregados
Al interactuar con modelos Eloquent, también puede usar los métodos count, sum, max y otros métodos agregados proporcionados por el query builder de Laravel. Como puede 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');
#Insertar y Actualizar 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 sencillo. Para insertar un nuevo registro en la base de datos, debe instanciar una nueva instancia del modelo y establecer atributos en el modelo. Luego, llame 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
{
/**
* Almacenar un nuevo vuelo en la base de datos.
*/
public function store(Request $request): RedirectResponse
{
// Validar 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. Las marcas 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 establecerlas manualmente.
Alternativamente, puede usar el método create para "guardar" un nuevo modelo usando una sola instrucción PHP. La instancia del modelo insertado 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á especificar una propiedad fillable o guarded en su clase de modelo. Estas propiedades son necesarias porque todos los modelos Eloquent están protegidos contra vulnerabilidades de asignación masiva por defecto. Para aprender más sobre asignación masiva, consulte la documentación de asignación masiva.
#Actualizaciones
El método save también puede usarse para actualizar modelos que ya existen en la base de datos. Para actualizar un modelo, debe recuperarlo y establecer los atributos que desea actualizar. Luego, debe llamar al método save del modelo. Nuevamente, la marca de tiempo updated_at se actualizará automáticamente, por lo que no es necesario establecer su valor manualmente:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->name = 'Paris to London';
$flight->save();
#Actualizaciones Masivas
Las actualizaciones también pueden realizarse en modelos que coincidan con una consulta dada. En este ejemplo, todos los vuelos que estén active y tengan un destination de San Diego serán marcados como retrasados:
Flight::where('active', 1)
->where('destination', 'San Diego')
->update(['delayed' => 1]);
El método update espera un array de pares columna y valor que representan las columnas que deben actualizarse. El método update devuelve el número de filas afectadas.
Al emitir una actualización masiva a través de Eloquent, los eventos del modelo saving, saved, updating y updated no se dispararán para los modelos actualizados. Esto se debe a que los modelos nunca se recuperan realmente cuando se emite una actualización masiva.
#Examinar Cambios en los Atributos
Eloquent proporciona los métodos isDirty, isClean y wasChanged para examinar el estado interno de su modelo y determinar cómo han cambiado sus atributos desde que el modelo fue recuperado originalmente.
El método isDirty determina si alguno de los atributos del modelo ha cambiado desde que el modelo fue recuperado. Puede 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 el modelo fue recuperado. Este método también acepta un argumento opcional de atributo:
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 algún atributo fue cambiado cuando el modelo fue guardado por última vez dentro del ciclo de la solicitud actual. Si es necesario, puede pasar un nombre de atributo para ver si un atributo en particular 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 sin importar los cambios realizados desde que fue recuperado. Si es necesario, puede 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; // john@example.com
$user->name = "Jack";
$user->name; // Jack
$user->getOriginal('name'); // John
$user->getOriginal(); // Array de atributos originales...
#Asignación Masiva
Puede usar el método create para "guardar" un nuevo modelo usando una sola instrucción PHP. La instancia del modelo insertado 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á especificar una propiedad fillable o guarded en su clase de modelo. Estas propiedades son necesarias porque todos los modelos Eloquent están protegidos contra vulnerabilidades de asignación masiva por defecto.
Una vulnerabilidad de asignación masiva ocurre cuando un usuario pasa un campo inesperado en una solicitud HTTP y ese campo cambia una columna en su base de datos que no esperaba. 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 su modelo, permitiendo al usuario escalar sus privilegios a administrador.
Por lo tanto, para comenzar, debe definir qué atributos del modelo desea hacer asignables masivamente. Puede hacer esto usando la propiedad $fillable en el modelo. Por ejemplo, hagamos que el atributo name de nuestro modelo Flight sea asignable masivamente:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
/**
* Los atributos que son asignables masivamente.
*
* @var array
*/
protected $fillable = ['name'];
}
Una vez que haya especificado qué atributos son asignables masivamente, puede 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 creado:
$flight = Flight::create(['name' => 'London to Paris']);
Si ya tiene una instancia del modelo, puede 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, cada clave asignable masivamente de la columna debe especificarse en el array $fillable de su modelo. Por seguridad, Laravel no soporta la actualización de atributos JSON anidados cuando se usa la propiedad guarded:
/**
* Los atributos que son asignables masivamente.
*
* @var array
*/
protected $fillable = [
'options->enabled',
];
#Permitir Asignación Masiva
Si desea hacer que todos sus atributos sean asignables masivamente, puede definir la propiedad $guarded de su modelo como un array vacío. Si elige desproteger su modelo, debe tener especial cuidado de siempre construir manualmente los arrays que se pasan a los métodos fill, create y update de Eloquent:
/**
* Los atributos que no son asignables masivamente.
*
* @var array
*/
protected $guarded = [];
#Excepciones en 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 el comportamiento esperado; sin embargo, durante el desarrollo local puede causar confusión sobre por qué los cambios en el modelo no surten efecto.
Si lo desea, puede indicar a Laravel que lance una excepción al intentar llenar un atributo no asignable invocando el método preventSilentlyDiscardingAttributes. Normalmente, este método debe invocarse dentro del método boot de uno de los proveedores de servicios de su aplicación:
use Illuminate\Database\Eloquent\Model;
/**
* Inicializar cualquier servicio de la aplicación.
*/
public function boot(): void
{
Model::preventSilentlyDiscardingAttributes($this->app->isLocal());
}
#Upserts
Ocasionalmente, puede necesitar actualizar un modelo existente o crear uno nuevo 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 siguiente ejemplo, si existe un vuelo con una ubicación de departure en Oakland y una ubicación de destination en San Diego, se actualizarán sus columnas price y discounted. Si no existe tal vuelo, se creará uno nuevo con los atributos resultantes de combinar el primer array argumento con el segundo array argumento:
$flight = Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
Si desea realizar múltiples "upserts" en una sola consulta, debe usar 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 forma única los registros dentro de la tabla asociada. El tercer y último argumento es un array de las columnas que deben actualizarse 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']);
Todas las 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 usa los índices "primary" y "unique" de la tabla para detectar registros existentes.
#Eliminar Modelos
Para eliminar un modelo, puede llamar al método delete en la instancia del modelo:
use App\Models\Flight;
$flight = Flight::find(1);
$flight->delete();
Puede llamar al método truncate para eliminar todos los registros asociados al modelo en la base de datos. La operación truncate también reiniciará cualquier ID autoincremental en la tabla asociada al modelo:
Flight::truncate();
#Eliminar un Modelo Existente por su Clave Primaria
En el ejemplo anterior, recuperamos el modelo de la base de datos antes de llamar al método delete. Sin embargo, si conoce la clave primaria del modelo, puede eliminarlo sin recuperarlo explícitamente llamando al método destroy. Además de aceptar una sola clave primaria, el método destroy acepta 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]));
El método destroy carga cada modelo individualmente y llama al método delete para que los eventos deleting y deleted se disparen correctamente para cada modelo.
#Eliminar Modelos Usando Consultas
Por supuesto, puede construir una consulta Eloquent para eliminar todos los modelos que coincidan con los criterios de su consulta. En este ejemplo, eliminaremos todos los vuelos que estén marcados como inactivos. Al igual que las actualizaciones masivas, las eliminaciones masivas no dispararán eventos de modelo para los modelos eliminados:
$deleted = Flight::where('active', 0)->delete();
Al ejecutar una sentencia de eliminación masiva a través de Eloquent, los eventos de modelo deleting y deleted no se dispararán para los modelos eliminados. Esto se debe a que los modelos nunca se recuperan realmente al ejecutar la sentencia de eliminación.
#Eliminación Suave (Soft Deleting)
Además de eliminar realmente registros de su base de datos, Eloquent también puede "eliminar suavemente" modelos. Cuando los modelos son eliminados suavemente, no se eliminan realmente de la base de datos. En su lugar, se establece un atributo deleted_at en el modelo que indica la fecha y hora en que el modelo fue "eliminado". Para habilitar la eliminación suave en un modelo, agregue el trait 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;
}
El trait SoftDeletes convertirá automáticamente el atributo deleted_at en una instancia DateTime / Carbon para usted.
También debe agregar la columna deleted_at a su tabla de base de datos. El schema builder de Laravel 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 llame al método delete en el modelo, la columna deleted_at se establecerá con la fecha y hora actuales. Sin embargo, el registro del modelo en la base de datos permanecerá en la tabla. Al consultar un modelo que usa eliminación suave, los modelos eliminados suavemente serán excluidos automáticamente de todos los resultados de la consulta.
Para determinar si una instancia de modelo dada ha sido eliminada suavemente, puede usar el método trashed:
if ($flight->trashed()) {
// ...
}
#Restaurar Modelos Eliminados Suavemente
A veces puede que desee "deseliminar" un modelo eliminado suavemente. Para restaurar un modelo eliminado suavemente, puede llamar al método restore en una instancia del modelo. El método restore establecerá la columna deleted_at del modelo a null:
$flight->restore();
También puede usar el método restore en una consulta para restaurar múltiples modelos. Nuevamente, como otras operaciones "masivas", esto no disparará eventos de modelo para los modelos restaurados:
Flight::withTrashed()
->where('airline_id', 1)
->restore();
El método restore también puede usarse al construir consultas de relaciones:
$flight->history()->restore();
#Eliminar Modelos Permanentemente
A veces puede necesitar eliminar realmente un modelo de su base de datos. Puede usar el método forceDelete para eliminar permanentemente un modelo eliminado suavemente de la tabla de la base de datos:
$flight->forceDelete();
También puede usar el método forceDelete al construir consultas de relaciones Eloquent:
$flight->history()->forceDelete();
#Consultar Modelos Eliminados Suavemente
#Incluir Modelos Eliminados Suavemente
Como se mencionó anteriormente, los modelos eliminados suavemente serán excluidos automáticamente de los resultados de la consulta. Sin embargo, puede 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 puede llamarse al construir una consulta de relación:
$flight->history()->withTrashed()->get();
#Recuperar Solo Modelos Eliminados Suavemente
El método onlyTrashed recuperará solo modelos eliminados suavemente:
$flights = Flight::onlyTrashed()
->where('airline_id', 1)
->get();
#Podar Modelos
A veces puede querer eliminar periódicamente modelos que ya no son necesarios. Para lograr esto, puede agregar el trait Illuminate\Database\Eloquent\Prunable o Illuminate\Database\Eloquent\MassPrunable a los modelos que desea podar periódicamente. Después de agregar uno de los traits al modelo, implemente un método prunable que devuelva un query builder 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;
/**
* Obtener la consulta de modelos podables.
*/
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subMonth());
}
}
Al marcar modelos como Prunable, también puede definir un método pruning en el modelo. Este método será llamado antes de que el modelo sea eliminado. Este método puede ser útil para eliminar recursos adicionales asociados con el modelo, como archivos almacenados, antes de que el modelo sea eliminado permanentemente de la base de datos:
/**
* Preparar el modelo para la poda.
*/
protected function pruning(): void
{
// ...
}
Después de configurar su modelo podable, debe programar el comando Artisan model:prune en la clase App\Console\Kernel de su aplicación. Usted es libre de elegir el intervalo apropiado en el que este comando debe ejecutarse:
/**
* Definir el calendario de comandos de la aplicación.
*/
protected function schedule(Schedule $schedule): void
{
$schedule->command('model:prune')->daily();
}
Detrás de escena, el comando model:prune detectará automáticamente los modelos "Prunable" dentro del directorio app/Models de su aplicación. Si sus modelos están en una ubicación diferente, puede 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 desea excluir ciertos modelos de ser podados mientras poda todos los demás modelos detectados, puede usar la opción --except:
$schedule->command('model:prune', [
'--except' => [Address::class, Flight::class],
])->daily();
Puede probar su consulta prunable ejecutando el comando model:prune con la opción --pretend. Al simular, el comando model:prune simplemente informará cuántos registros serían podados si el comando se ejecutara realmente:
php artisan model:prune --pretend
Los modelos eliminados suavemente serán eliminados permanentemente (forceDelete) si coinciden con la consulta prunable.
#Poda Masiva
Cuando los modelos están marcados con el trait Illuminate\Database\Eloquent\MassPrunable, los modelos se eliminan de la base de datos usando consultas de eliminación masiva. Por lo tanto, el método pruning no será invocado, ni se disparará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;
/**
* Obtener la consulta de modelos podables.
*/
public function prunable(): Builder
{
return static::where('created_at', '<=', now()->subMonth());
}
}
#Replicar Modelos
Puede crear una copia no guardada de una instancia de modelo existente usando el método replicate. Este método es particularmente útil cuando tiene instancias de modelo que comparten muchos de los mismos 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, puede 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'
]);
#Alcances de Consulta (Query Scopes)
#Alcances Globales
Los alcances globales le permiten agregar restricciones a todas las consultas para un modelo dado. La propia funcionalidad de eliminación suave de Laravel utiliza alcances globales para recuperar solo modelos "no eliminados" de la base de datos. Escribir sus propios alcances globales puede proporcionar una forma conveniente y fácil de asegurarse de que cada consulta para un modelo dado reciba ciertas restricciones.
#Generar Alcances
Para generar un nuevo alcance global, puede invocar el comando Artisan make:scope, que colocará el alcance generado en el directorio app/Models/Scopes de su aplicación:
php artisan make:scope AncientScope
#Escribir Alcances Globales
Escribir un global scope es sencillo. Primero, use el comando make:scope para generar una clase que implemente la interfaz Illuminate\Database\Eloquent\Scope. La interfaz Scope requiere que implemente un método: apply. El método apply puede agregar restricciones where u otro tipo 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
{
/**
* Aplicar el scope a un constructor de consultas Eloquent dado.
*/
public function apply(Builder $builder, Model $model): void
{
$builder->where('created_at', '<', now()->subYears(2000));
}
}
Si su global scope agrega columnas a la cláusula select de la consulta, debe usar el método addSelect en lugar de select. Esto evitará la sustitución no intencionada de la cláusula select existente en la consulta.
#Aplicando Global Scopes
Para asignar un global scope a un modelo, simplemente puede colocar el atributo ScopedBy en el modelo:
<?php
namespace App\Models;
use App\Models\Scopes\AncientScope;
use Illuminate\Database\Eloquent\Attributes\ScopedBy;
#[ScopedBy([AncientScope::class])]
class User extends Model
{
//
}
O bien, puede registrar manualmente el global scope sobrescribiendo el método booted del modelo e invocando el método addGlobalScope del modelo. El método addGlobalScope acepta una instancia de su scope como ú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 scope 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
#Global Scopes Anónimos
Eloquent también permite definir global scopes usando closures, lo cual es especialmente útil para scopes simples que no justifican una clase separada. Al definir un global scope usando un closure, debe proporcionar un nombre para el scope como primer argumento del 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));
});
}
}
#Eliminando Global Scopes
Si desea eliminar un global scope para una consulta dada, puede usar el método withoutGlobalScope. Este método acepta como único argumento el nombre de la clase del global scope:
User::withoutGlobalScope(AncientScope::class)->get();
O, si definió el global scope usando un closure, debe pasar el nombre en cadena que asignó al global scope:
User::withoutGlobalScope('ancient')->get();
Si desea eliminar varios o incluso todos los global scopes de la consulta, puede usar el método withoutGlobalScopes:
// Eliminar todos los global scopes...
User::withoutGlobalScopes()->get();
// Eliminar algunos global scopes...
User::withoutGlobalScopes([
FirstScope::class, SecondScope::class
])->get();
#Scopes locales
Los local scopes le permiten definir conjuntos comunes de restricciones de consulta que puede reutilizar fácilmente en toda su aplicación. Por ejemplo, puede necesitar recuperar frecuentemente todos los usuarios considerados "populares". Para definir un scope, anteponga scope al nombre del método del modelo Eloquent.
Los scopes siempre deben devolver la misma instancia del constructor de consultas o void:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope para incluir solo usuarios populares.
*/
public function scopePopular(Builder $query): void
{
$query->where('votes', '>', 100);
}
/**
* Scope para incluir solo usuarios activos.
*/
public function scopeActive(Builder $query): void
{
$query->where('active', 1);
}
}
#Utilizando un Local Scope
Una vez definido el scope, puede llamar a los métodos del scope al consultar el modelo. Sin embargo, no debe incluir el prefijo scope al llamar al método. Incluso puede encadenar llamadas a varios scopes:
use App\Models\User;
$users = User::popular()->active()->orderBy('created_at')->get();
Combinar múltiples scopes de modelos Eloquent mediante un operador de consulta or puede requerir el uso de closures para lograr la correcta agrupación lógica:
$users = User::popular()->orWhere(function (Builder $query) {
$query->active();
})->get();
Sin embargo, dado que esto puede ser engorroso, Laravel proporciona un método orWhere de "orden superior" que le permite encadenar scopes de forma fluida sin usar closures:
$users = User::popular()->orWhere->active()->get();
#Scopes Dinámicos
A veces puede querer definir un scope que acepte parámetros. Para comenzar, simplemente agregue sus parámetros adicionales a la firma del método del scope. Los parámetros del scope deben definirse después del parámetro $query:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Scope para incluir solo usuarios de un tipo dado.
*/
public function scopeOfType(Builder $query, string $type): void
{
$query->where('type', $type);
}
}
Una vez que haya agregado los argumentos esperados a la firma del método del scope, puede pasar los argumentos al llamar al scope:
$users = User::ofType('admin')->get();
#Comparando Modelos
A veces puede necesitar determinar si dos modelos son "iguales" o no. Los métodos is y isNot pueden usarse para verificar rápidamente si dos modelos tienen la misma clave primaria, tabla y conexión a la 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 especialmente útil cuando desea comparar un modelo relacionado sin emitir una consulta para recuperar ese modelo:
if ($post->author()->is($user)) {
// ...
}
#Eventos
¿Desea transmitir sus eventos Eloquent directamente a su aplicación del lado cliente? Consulte la transmisión de eventos de modelo de Laravel.
Los modelos Eloquent despachan varios eventos, permitiéndole engancharse 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 despacha cuando un modelo existente es recuperado de la base de datos. Cuando un nuevo modelo se guarda por primera vez, se despachan los eventos creating y created. Los eventos updating / updated se despachan cuando un modelo existente es modificado y se llama al método save. Los eventos saving / saved se despachan cuando un modelo es creado o actualizado, incluso si los atributos del modelo no han cambiado. Los nombres de eventos que terminan en -ing se despachan antes de que cualquier cambio en el modelo sea persistido, mientras que los eventos que terminan en -ed se despachan después de que los cambios en el modelo son persistidos.
Para comenzar a escuchar eventos de modelo, defina una propiedad $dispatchesEvents en su modelo Eloquent. Esta propiedad mapea varios puntos del ciclo de vida del modelo Eloquent a sus 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 mapear sus eventos Eloquent, puede usar listeners de eventos para manejar los eventos.
Al emitir una consulta de actualización o eliminación masiva vía Eloquent, los eventos de modelo saved, updated, deleting y deleted no se despacharán para los modelos afectados. Esto se debe a que los modelos nunca se recuperan realmente al realizar actualizaciones o eliminaciones masivas.
#Usando Closures
En lugar de usar clases de eventos personalizadas, puede registrar closures que se ejecuten cuando se despachen varios eventos de modelo. Normalmente, debe registrar estos closures en el método booted de su 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, puede utilizar listeners anónimos en cola al registrar eventos de modelo. Esto indicará a Laravel que ejecute el listener del evento del modelo en segundo plano usando la cola de su aplicación:
use function Illuminate\Events\queueable;
static::created(queueable(function (User $user) {
// ...
}));
#Observadores
#Definiendo Observadores
Si está escuchando muchos eventos en un modelo dado, puede usar observadores para agrupar todos sus listeners en una sola clase. Las clases observadoras tienen nombres de métodos que reflejan los eventos Eloquent que desea escuchar. Cada uno de estos métodos recibe el modelo afectado como único argumento. El comando Artisan make:observer es la forma más fácil de crear una nueva clase observadora:
php artisan make:observer UserObserver --model=User
Este comando colocará el nuevo observador en su directorio app/Observers. Si este directorio no existe, Artisan lo creará por usted. Su nuevo observador se verá así:
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
/**
* Manejar el evento "created" de User.
*/
public function created(User $user): void
{
// ...
}
/**
* Manejar el evento "updated" de User.
*/
public function updated(User $user): void
{
// ...
}
/**
* Manejar el evento "deleted" de User.
*/
public function deleted(User $user): void
{
// ...
}
/**
* Manejar el evento "restored" de User.
*/
public function restored(User $user): void
{
// ...
}
/**
* Manejar el evento "forceDeleted" de User.
*/
public function forceDeleted(User $user): void
{
// ...
}
}
Para registrar un observador, puede colocar el atributo ObservedBy en el modelo correspondiente:
use App\Observers\UserObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
#[ObservedBy([UserObserver::class])]
class User extends Authenticatable
{
//
}
O bien, puede registrar manualmente un observador llamando al método observe en el modelo que desea observar. Puede registrar observadores en el método boot del proveedor de servicios App\Providers\EventServiceProvider de su aplicación:
use App\Models\User;
use App\Observers\UserObserver;
/**
* Registrar cualquier evento para su aplicación.
*/
public function boot(): void
{
User::observe(UserObserver::class);
}
Hay eventos adicionales 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 los modelos se crean dentro de una transacción de base de datos, puede querer indicar a un observador que solo ejecute sus manejadores de eventos después de que la transacción se haya confirmado. Puede lograr esto implementando la interfaz ShouldHandleEventsAfterCommit en su observador. Si no hay una transacción en curso, los manejadores de eventos se ejecutarán inmediatamente:
<?php
namespace App\Observers;
use App\Models\User;
use Illuminate\Contracts\Events\ShouldHandleEventsAfterCommit;
class UserObserver implements ShouldHandleEventsAfterCommit
{
/**
* Manejar el evento "created" de User.
*/
public function created(User $user): void
{
// ...
}
}
#Silenciar Eventos
Ocasionalmente puede necesitar "silenciar" temporalmente todos los eventos disparados por un modelo. Puede lograr esto usando el método withoutEvents. El método withoutEvents acepta un closure como único argumento. Cualquier código ejecutado dentro de este closure no despachará eventos de modelo, y cualquier valor retornado por el closure será retornado por el método withoutEvents:
use App\Models\User;
$user = User::withoutEvents(function () {
User::findOrFail(1)->delete();
return User::find(2);
});
#Guardar un Modelo Individual Sin Eventos
A veces puede querer "guardar" un modelo dado sin despachar ningún evento. Puede lograr esto usando el método saveQuietly:
$user = User::findOrFail(1);
$user->name = 'Victoria Faith';
$user->saveQuietly();
También puede "actualizar", "eliminar", "eliminar suavemente", "restaurar" y "replicar" un modelo dado sin despachar ningún evento:
$user->deleteQuietly();
$user->forceDeleteQuietly();
$user->restoreQuietly();