Documentación de Laravel 10.x
Aquí encontrarás fragmentos de código de Laravel y consejos útiles sobre desarrollo web.
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.
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.
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 --factoryphp artisan make:model Flight -f # Generate a model and a FlightSeeder class...php artisan make:model Flight --seedphp artisan make:model Flight -s # Generate a model and a FlightController class...php artisan make:model Flight --controllerphp artisan make:model Flight -c # Generate a model, FlightController resource class, and form request classes...php artisan make:model Flight --controller --resource --requestsphp 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 --pivotphp artisan make:model Member -p
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
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{ // ...}
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';}
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';}
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.
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"
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']));
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';}
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, ];}
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());
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;}
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.
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"
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;}
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');
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
.
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étodolazy
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.
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();
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();
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 () { // ...});
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);});
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']);
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');
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.
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();
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
yupdated
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.
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->name = "Jack";$user->name; // Jack $user->getOriginal('name'); // John$user->getOriginal(); // Array of original attributes...
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']);
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',];
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 = [];
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());}
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étodoupsert
y siempre utiliza los índices "primary" y "unique" de la tabla para detectar registros existentes.
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();
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étododelete
para que los eventos de modelodeleting
ydeleted
se envíen correctamente para cada modelo.
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
ydeleted
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.
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 atributodeleted_at
en una instancia deDateTime
/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()) { // ...}
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();
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();
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();
El método onlyTrashed
recuperará solo los modelos eliminados suavemente:
$flights = Flight::onlyTrashed() ->where('airline_id', 1) ->get();
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.
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()); }}
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']);
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.
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
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 deselect
. Esto evitará la sustitución involuntaria de la cláusula select existente de la consulta.
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
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)); }); }}
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();
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); }}
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();
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();
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)) { // ...}
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
ydeleted
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.
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) { // ...}));
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
yretrieved
. Estos eventos se describen en la documentación de eventos.
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 { // ... }}
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);});
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();