- Introducción
- Definiendo modelos
- Obteniendo modelos
- Obteniendo modelos individuales / Agrupamientos
- Insertando y actualizando modelos
- Eliminando modelos
- Replicando modelos
- Alcances de consulta
- Comparando modelos
- Eventos
Introducción
El ORM Eloquent incluido con Laravel proporciona una genial y simple implentación básica de ActiveRecord para trabajar con tu base de datos. Cada tabla de base de datos tiene un correspondiente «Modelo» el cual es usado para interactuar con la tabla. Los modelos permiten que consultes los datos en tus tablas, así como también insertar nuevos registros dentro de la tabla.
Antes de empezar, asegúrate de configurar una conexión de base de datos en config/database.php
. Para mayor información sobre la configuración de tu base de datos, revisa la documentación.
Definiendo modelos
Para empezar, vamos a crear un modelo de Eloquent. Los modelos residen típicamente en el directorio app
, pero eres libre de colocarlos en cualquier parte que pueda ser auto-cargada de acuerdo a tu archivo composer.json
. Todos los modelos de Eloquent extienden la clase Illuminate\Database\Eloquent\Model
.
La forma más fácil de crear una instancia del modelo es usando el Comando Artisan make:model
:
php artisan make:model Flight
Si prefieres generar una migración de base de datos cuando generes el modelo, puedes usar la opción --migration
o -m
:
php artisan make:model Flight --migration php artisan make:model Flight -m
Convenciones del modelo Eloquent
Ahora, vamos a mirar un modelo Flight
de ejemplo, el cual usaremos para obtener y guardar información desde nuestra tabla de base de datos flights
:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { // }
Nombres de tabla
Observa que no le dijimos a Eloquent cuál tabla usar para nuestro modelo Flight
. Por convención, el nombre de la clase en plural y en formato «snake_case» será usado como el nombre de tabla a menos que otro nombre sea especificado expresamente. Así, en este caso, Eloquent asumirá que el modelo Flight
guarde los registros en la tabla flights
. Puedes especificar una tabla personalizada al definir una propiedad table
en tu modelo:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * The table associated with the model. * * @var string */ protected $table = 'my_flights'; }
Claves primarias
Eloquent asumirá que cada tabla tiene una columna de clave primaria denominada id
. Puedes definir una propiedad $primaryKey
protegida para sobrescribir esta convención:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * The primary key associated with the table. * * @var string */ protected $primaryKey = 'flight_id'; }
Además, Eloquent asume que la clave primaria es un valor entero autoincremental, lo que significa que de forma predeterminada la clave primaria será convertida a un tipo int
automáticamente. Si deseas usar una clave primaria que no sea de autoincremento o numérica debes establecer la propiedad pública $incrementing
de tu modelo a false
:
<?php class Flight extends Model { /** * Indicates if the IDs are auto-incrementing. * * @var bool */ public $incrementing = false; }
Si tu clave primaria no es un entero, debes establecer la propiedad protegida $keyType
de tu modelo a string
:
<?php class Flight extends Model { /** * The "type" of the auto-incrementing ID. * * @var string */ protected $keyType = 'string'; }
Marcas de tiempo o timestamps
De forma predeterminada, Eloquent espera que las columnas created_at
y updated_at
existan en tus tablas. Si no deseas tener estas columnas manejadas automáticamente por Eloquent, establece la propiedad $timestamps
de tu modelo a false
:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * Indicates if the model should be timestamped. * * @var bool */ public $timestamps = false; }
Si necesitas personalizar el formato de tus marcas de tiempo, establece la propiedad $dateFormat
de tu modelo. Esta propiedad determina como los atributos de fecha son guardados en la base de datos, también como su formato cuando el modelo es serializado a un arreglo o JSON:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * The storage format of the model's date columns. * * @var string */ protected $dateFormat = 'U'; }
Si necesitas personalizar los nombres de las columnas usadas para guardar las marcas de tiempo, puedes establecer las constantes CREATED_AT
y UPDATED_AT
en tu modelo:
<?php class Flight extends Model { const CREATED_AT = 'creation_date'; const UPDATED_AT = 'last_update'; }
Conexión de base de datos
De forma predeterminada, todos los modelos Eloquent usarán la conexión de base de datos configurada por tu aplicación. Si quieres especificar una conexión diferente para el modelo, usa la propiedad $connection
:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * The connection name for the model. * * @var string */ protected $connection = 'connection-name'; }
Valores de atributo predeterminados
Si deseas definir los valores predeterminados para algunos de los atributos de tu modelo, puedes definir una propiedad $attributes
en tu modelo:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * The model's default values for attributes. * * @var array */ protected $attributes = [ 'delayed' => false, ]; }
Obteniendo modelos
Una vez que has creado un modelo y su tabla de base de datos asociada, estás listo para empezar a obtener datos de tu base de datos. Piensa en cada modelo de Eloquent como un constructor de consultas muy poderoso que te permite consultar fluidamente la tabla de base de datos asociada con el modelo. Por ejemplo:
<?php $flights = App\Flight::all(); foreach ($flights as $flight) { echo $flight->name; }
Añadiendo restricciones adicionales
El método all
de Eloquent devolverá todos los resultados en la tabla del modelo. Ya que cada modelo de Eloquent sirve como un constructor de consultas, también puedes añadir restricciones a las consultas y entonces usar el método get
para obtener los resultados:
$flights = App\Flight::where('active', 1) ->orderBy('name', 'desc') ->take(10) ->get();
Ya que los modelos de Eloquent son constructores de consultas, deberías revisar todos los métodos disponibles en el constructor de consultas. Puedes usar cualquiera de estos métodos en tus consultas de Eloquent.
Actualizando modelos
Podemos actualizar modelos usando los métodos fresh
y refresh
. El método fresh
volverá a recuperar el modelo de la base de datos. La instancia de modelo existente no será afectada:
$flight = App\Flight::where('number', 'FR 900')->first(); $freshFlight = $flight->fresh();
El método refresh
«rehidratará» el modelo existente usando datos nuevos de la base de datos. Además, todas sus relaciones cargadas previamente serán también actualizadas:
$flight = App\Flight::where('number', 'FR 900')->first(); $flight->number = 'FR 456'; $flight->refresh(); $flight->number; // "FR 900"
Colecciones
Para métodos de Eloquent como all
y get
que obtienen varios resultados, se devolverá una instancia de Illuminate\Database\Eloquent\Collection
. La clase Collection
proporciona una variedad de métodos útiles para trabajar con los resultados de Eloquent:
$flights = $flights->reject(function ($flight) { return $flight->cancelled; });
También puedes recorrer la colección como un arreglo:
foreach ($flights as $flight) { echo $flight->name; }
Resultados divididos en partes (chunk)
Si necesitas procesar miles de registros de Eloquent, usa el comando chunk
. El método chunk
obtendrá una «porción» de los modelos de Eloquent, incorporándolos a una Closure
dada para procesamiento. Usando el método chunk
ahorrarás memoria al momento de trabajar con grandes conjuntos de resultados:
Flight::chunk(200, function ($flights) { foreach ($flights as $flight) { // } });
El primer argumento pasado al método es el número de registros que deseas obtener por cada «porción». La Closure pasada como segundo argumento será ejecutada para cada porción que sea obtenida de la base de datos. Una consulta de base de datos será ejecutada para obtener cada porción de registros pasados a la Closure.
Usando cursores
El método cursor
permite que iteres a través de registros de tu base de datos usando un cursor, el cual ejecutará solamente una consulta única. Al momento de procesar grandes cantidades de datos, puedes usar el método cursor
para reducir en gran medida el uso de la memoria:
foreach (Flight::where('foo', 'bar')->cursor() as $flight) { // }
El cursor
retorna una instancia Illuminate\Support\LazyCollection
. Las colecciones lazy te permiten usar muchos de los métodos de colección disponibles en colecciones típicas de Laravel mientras que sólo carga un único modelo en memoria a la vez:
$users = App\User::cursor()->filter(function ($user) { return $user->id > 500; }); foreach ($users as $user) { echo $user->id; }
Subconsultas avanzadas
Selects de subconsultas
Eloquent también ofrece soporte avanzado para subconsultas, lo que te permite extraer información de tablas relacionadas en una sola consulta. Por ejemplo, imaginemos que tenemos una tabla de destinos de vuelos destinations
y una tabla de vuelos flights
hacia los destinos. La tabla flights
contiene una columna arrived_at
que indica cuando el vuelo ha arribado al destino.
Usando la funcionalidad de subconsulta disponible en los métodos select
y addSelect
, podemos seleccionar todos los destinations
(destinos) y el nombre del vuelo que más recientemente arribó a dicho destino usando una única consulta:
use App\Destination; use App\Flight; return Destination::addSelect(['last_flight' => Flight::select('name') ->whereColumn('destination_id', 'destinations.id') ->orderBy('arrived_at', 'desc') ->limit(1) ])->get();
Ordenando subconsultas
Adicionalmente, la función orderBy
del constructor de consultas soporta subconsultas. Podemos usar esta funcionalidad para ordenar los destinos en base a cuando llegó el último vuelo a dicho destino. De nuevo, esto puede ser hecho ejecutando una única consulta en la base de datos:
return Destination::orderByDesc( Flight::select('arrived_at') ->whereColumn('destination_id', 'destinations.id') ->orderBy('arrived_at', 'desc') ->limit(1) )->get();
Obteniendo modelos individuales / Agrupamientos
Además de obtener todos los registros de una tabla dada, también puedes obtener registros individuales usando find
, first
o firstWhere
. En lugar de devolver una colección de modelos, estos métodos devuelven una única instancia de modelo:
// Retrieve a model by its primary key... $flight = App\Flight::find(1); // Retrieve the first model matching the query constraints... $flight = App\Flight::where('active', 1)->first(); // Shorthand for retrieving the first model matching the query constraints... $flight = App\Flight::firstWhere('active', 1);
También puedes ejecutar el método find
con un arreglo de claves primarias, el cual devolverá una colección de los registros que coincidan:
$flights = App\Flight::find([1, 2, 3]);
Algunas veces puedes querer retornar el primer resultado de una consulta o realizar alguna otra acción si ningún resultado es encontrado. El método firstOr
retornará el primer resultado encontrado o, si ningún resultado es encontrado, ejecutará el callback dado. El resultado del callback será considerado el resultado del método firstOr
:
$model = App\Flight::where('legs', '>', 100)->firstOr(function () { // ... });
El método firstOr
también acepta un arreglo de columnas a ser retornadas:
$model = App\Flight::where('legs', '>', 100) ->firstOr(['id', 'legs'], function () { // ... });
Excepciones not found (no encontrado)
Algunas veces, puedes desear arrojar una excepción si un modelo no es encontrado. Es particularmente útil en rutas o controladores. Los métodos findOrFail
y firstOrFail
obtendrán el primer resultado de la consulta; sin embargo, si nada es encontrado, una excepción de Illuminate\Database\Eloquent\ModelNotFoundException
será arrojada:
$model = App\Flight::findOrFail(1); $model = App\Flight::where('legs', '>', 100)->firstOrFail();
Si la excepción no es atrapada, una respuesta HTTP 404
es enviada automáticamente de regreso al usuario. No es necesario escribir verificaciones explícitas para devolver respuestas 404
cuando uses estos métodos:
Route::get('/api/flights/{id}', function ($id) { return App\Flight::findOrFail($id); });
Obteniendo agrupamientos
También puedes usar los métodos count
, sum
, max
y otros métodos de agrupamiento proporcionados por el constructor de consulta. Estos métodos devuelven el valor escalar apropiado en lugar de una completa instancia de modelo:
$count = App\Flight::where('active', 1)->count(); $max = App\Flight::where('active', 1)->max('price');
Insertando Y actualizando modelos
Inserciones
Para agregar un nuevo registro en la base de datos crea una nueva instancia de modelo, establece los atributos del modelo y después ejecuta el método save
:
<?php namespace App\Http\Controllers; use App\Http\Controllers\Controller; use App\Flight; use Illuminate\Http\Request; class FlightController extends Controller { /** * Create a new flight instance. * * @param Request $request * @return Response */ public function store(Request $request) { // Validate the request... $flight = new Flight; $flight->name = $request->name; $flight->save(); } }
En este ejemplo, asignamos el parámetro name
de la solicitud entrante al atributo name
de la instancia del modelo App\Flight
. Cuando ejecutamos el método save
, un registro será insertado en la base de datos. Las marcas de tiempo created_at
y updated_at
serán automáticamente establecidas cuando el método save
sea ejecutado, no hay necesidad de establecerlos manualmente.
Actualizaciones
El método save
también puede ser usado para actualizar modelos que ya existen en la base de datos. Para actualizar un modelo, debes obtenerlo, establecer cualquiera de los atributos que desees actualizar y después ejecutar el método save
. Otra vez, la marca de tiempo updated_at
será actualizada automáticamente, no hay necesidad de establecer su valor manualmente.
$flight = App\Flight::find(1); $flight->name = 'New Flight Name'; $flight->save();
Actualizaciones masivas
Las actualizaciones también pueden ser ejecutadas contra cualquier número de modelos que coincidan con un criterio de consulta dada. En este ejemplo, todos los vuelos que están activos o con active
igual a 1 y tienen un atributo destination
igual a San Diego
serán marcados como retrasados:
App\Flight::where('active', 1) ->where('destination', 'San Diego') ->update(['delayed' => 1]);
El método update
espera un arreglo de pares de columna y valor representando las columnas que deberían ser actualizadas.
Al momento de utilizar una actualización masiva por medio de Eloquent, los eventos de modelo saving
, saved
, updating
y updated
no serán disparados para los modelos actualizados. Esto es debido a que los modelos nunca son obtenidos en realidad al momento de hacer una actualización masiva.
Examinando cambios en los atributos
Eloquent proporciona los métodos isDirty
, isClean
y wasChanged
para examinar el estado interno de tus modelos y determinar cómo sus atributos han cambiado desde que fueron originalmente cargados.
El método isDirty
determina si algún atributo ha cambiado desde que el modelo fue cargado. Puedes pasar un nombre de atributo específico para determinar si un atributo particular está sucio. El método isClean
es el opuesto a isDirty
y también acepta un atributo opcional como argumento:
$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->isClean(); // false $user->isClean('title'); // false $user->isClean('first_name'); // true $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 solicitud actual. También puedes pasar un nombre de atributo para ver si un atributo particular ha 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('first_name'); // false
Asignación masiva
También puedes usar el método create
para guardar un nuevo modelo en una sola línea. La instancia de modelo insertada te será devuelta por el método. Sin embargo, antes de hacer eso, necesitarás especificar o un atributo fillable
o guarded
del modelo, de modo que todos los modelos de Eloquent se protejan contra la asignación masiva de forma predeterminada.
Una vulnerabilidad en la asignación masiva ocurre cuando un usuario pasa un parámetro HTTP inesperado a través de una solicitud y ese parámetro cambia una columna en tu base de datos que no esperabas. Por ejemplo, un usuario malicioso podría enviar un parámetro is_admin
a través de una solicitud HTTP, la cual es entonces pasada en el método create
de tu modelo, permitiendo que el usuario se promueva a sí mismo como un usuario administrador.
Así que, para empezar, debes definir cuáles atributos del modelo quieres que se asignen de forma masiva. Puedes hacerlo usando la propiedad $fillable
del modelo. Por ejemplo, vamos a hacer que el atributo name
de nuestro modelo Flight
sea asignado masivamente:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * The attributes that are mass assignable. * * @var array */ protected $fillable = ['name']; }
Una vez que hemos indicado los atributos asignables en masa, podemos usar el método create
para insertar un nuevo registro en la base de datos. El método create
devuelve la instancia de modelo guardada:
$flight = App\Flight::create(['name' => 'Flight 10']);
Si ya tienes una instancia del modelo, puedes usar el método fill
para llenarla con un arreglo de atributos:
$flight->fill(['name' => 'Flight 22']);
Protección de atributos
Mientras $fillable
sirve como una «lista blanca» de atributos que deben ser asignados en forma masiva, también puedes elegir usar $guarded
. La propiedad $guarded
debe contener un arreglo de atributos que no deseas que sean asignados en forma masiva. El resto de atributos que no estén en el arreglo serán asignados masivamente. Por lo tanto, $guarded
funciona como una «lista negra». Es importante destacar que debes usar $fillable
o $guarded
– pero no ambos. En el ejemplo siguiente, todos los atributos excepto price
serán asignados en forma masiva:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Flight extends Model { /** * The attributes that aren't mass assignable. * * @var array */ protected $guarded = ['price']; }
Si prefieres hacer que todos los atributos sean asignados masivamente, puedes definir la propiedad $guarded
como un arreglo vacío:
/** * The attributes that aren't mass assignable. * * @var array */ protected $guarded = [];
Otros métodos de creación
firstOrCreate
/ firstOrNew
Hay otros dos métodos que puedes usar para crear modelos con atributos de asignación masiva: firstOrCreate
y firstOrNew
. El método firstOrCreate
intentará localizar un registro de base de datos usando los pares columna / valor dados. Si el modelo no puede ser encontrado en la base de datos, un registro será insertado con los atributos del primer parámetro, junto con aquellos del segundo parámetro 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 un modelo no es encontrado, una nueva instancia de modelo será devuelta. Nota que el modelo devuelto por firstOrNew
todavía no ha sido enviado a la base de datos. Necesitarás ejecutar save
manualmente para hacerlo persistente:
// Retrieve flight by name, or create it if it doesn't exist... $flight = App\Flight::firstOrCreate(['name' => 'Flight 10']); // Retrieve flight by name, or create it with the name, delayed, and arrival_time attributes... $flight = App\Flight::firstOrCreate( ['name' => 'Flight 10'], ['delayed' => 1, 'arrival_time' => '11:30'] ); // Retrieve by name, or instantiate... $flight = App\Flight::firstOrNew(['name' => 'Flight 10']); // Retrieve by name, or instantiate with the name, delayed, and arrival_time attributes... $flight = App\Flight::firstOrNew( ['name' => 'Flight 10'], ['delayed' => 1, 'arrival_time' => '11:30'] );
updateOrCreate
También puedes encontrar situaciones donde quieras actualizar un modelo existente o crear un nuevo modelo si no existe. Laravel proporciona un método updateOrCreate
para hacer esto en un paso. Al igual que el método firstOrCreate
, updateOrCreate
persiste el modelo, para que no haya necesidad de ejecutar save()
:
// If there's a flight from Oakland to San Diego, set the price to $99. // If no matching model exists, create one. $flight = App\Flight::updateOrCreate( ['departure' => 'Oakland', 'destination' => 'San Diego'], ['price' => 99, 'discounted' => 1] );
Eliminando modelos
Para eliminar un modelo, ejecuta el método delete
en una instancia del modelo:
$flight = App\Flight::find(1); $flight->delete();
Eliminando un modelo existente por clave
En el ejemplo anterior, estamos obteniendo el modelo de la base de datos antes de ejecutar el método delete
. Sin embargo, si conoces la clave primaria del modelo, puedes eliminar el modelo sin obtenerlo primero. Para hacer eso, ejecuta el método destroy
. Además de recibir una sola clave primaria como argumento, el método destroy
aceptará múltiples claves primarias, un arreglo de claves primarias, o una colección de claves primarias:
App\Flight::destroy(1); App\Flight::destroy(1, 2, 3); App\Flight::destroy([1, 2, 3]); App\Flight::destroy(collect([1, 2, 3]));
Eliminando modelos por consultas
También puedes ejecutar una instrucción de eliminar en un conjunto de modelos. 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 cualquiera de los eventos de modelo para los modelos que son eliminados:
$deletedRows = App\Flight::where('active', 0)->delete();
Al momento de ejecutar una instrucción de eliminación masiva por medio de Eloquent, los eventos de modelo deleting
and deleted
no serán ejecutados para los modelos eliminados. Esto es debido a que los modelos nunca son obtenidos realmente al momento de ejecutar la instrucción de eliminación.
Eliminación lógica (Soft Deleting)
Además de eliminar realmente los registros de tu base de datos, Eloquent también puede «eliminar lógicamente» los modelos. Cuando los modelos son borrados lógicamente, no son removidos realmente de tu base de datos. En lugar de eso, un atributo deleted_at
es establecido en el modelo e insertado en la base de datos. Si un modelo tiene un valor deleted_at
no nulo, el modelo ha sido eliminado lógicamente. Para habilitar eliminaciones lógicas en un modelo, usa el trait Illuminate\Database\Eloquent\SoftDeletes
en el modelo:
<?php namespace App; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; class Flight extends Model { use SoftDeletes; }
El trait SoftDeletes
convertirá (cast) automáticamente el atributo deleted_at
a una instancia de DateTime
/ Carbon
para ti.
Debes añadir la columna deleted_at
a tu tabla de base de datos. El constructor de esquemas de Laravel contiene un método helper para crear esta columna:
Schema::table('flights', function (Blueprint $table) { $table->softDeletes(); });
Ahora, cuando ejecutes el método delete
en el modelo, la columna deleted_at
será establecida con la fecha y hora actual. Y, al momento de consultar un modelo que use eliminaciones lógicas, los modelos eliminados lógicamente serán excluidos automáticamente de todos los resultados de consultas.
Para determinar si una instancia de modelo ha sido eliminada lógicamente, usa el método trashed
:
if ($flight->trashed()) { // }
Consultando modelos eliminados lógicamente
Incluyendo modelos eliminados lógicamente
Como se apreció anteriormente, los modelos eliminados lógicamente serán excluidos automáticamente de los resultados de las consultas. Sin embargo, puedes forzar que los modelos eliminados lógicamente aparezcan en un conjunto resultante usando el método withTrashed
en la consulta:
$flights = App\Flight::withTrashed() ->where('account_id', 1) ->get();
El método withTrashed
también puede ser usado en una consulta de relación de Eloquent:
$flight->history()->withTrashed()->get();
Obteniendo modelos individuales eliminados lógicamente
El método onlyTrashed
obtendrá solamente modelos eliminados lógicamente:
$flights = App\Flight::onlyTrashed() ->where('airline_id', 1) ->get();
Restaurando modelos eliminados lógicamente
Algunas veces puedes desear «deshacer la eliminación» de un modelo eliminado lógicamente. Para restaurar un modelo eliminado lógicamente a un estado activo, usa el método restore
en una instancia de modelo:
$flight->restore();
También puedes usar el método restore
en una consulta para restaurar rápidamente varios modelos. Otra vez, al igual que otras operaciones «masivas», esto no disparará cualquiera de los eventos de modelo para los modelos que sean restaurados:
App\Flight::withTrashed() ->where('airline_id', 1) ->restore();
Al igual que con el método withTrashed
, el método restore
también puede ser usado en relaciones de Eloquent:
$flight->history()->restore();
Eliminando modelos permanentemente
Algunas veces puedes necesitar eliminar verdaderamente un modelo de tu base de datos. Para remover permanentemente un modelo eliminado lógicamente de la base de datos, usa el método forceDelete
:
// Force deleting a single model instance... $flight->forceDelete(); // Force deleting all related models... $flight->history()->forceDelete();
Replicando modelos
Puedes crear una copia no guardada de una instancia de modelo usando el método replicate
. Esto es particularmente útil cuando tienes instancias de modelo que comparten muchos de los mismos atributos:
$shipping = App\Address::create([ 'type' => 'shipping', 'line_1' => '123 Example Street', 'city' => 'Victorville', 'state' => 'CA', 'postcode' => '90001', ]); $billing = $shipping->replicate()->fill([ 'type' => 'billing' ]); $billing->save();
Alcances (Scopes) de consultas
Alcances (scopes) globales
Los alcances globales permiten que añadas restricciones a todas las consultas para un modelo dado. La propia funcionalidad de la eliminación lógica de Laravel utiliza alcances globales para extraer solamente los modelos «no-eliminados» de la base de datos. Escribir tus propios alcances globales puede proporcionarte una forma conveniente y fácil de asegurar que cada consulta para un modelo dado reciba ciertas restricciones.
Escribiendo scopes globales
Escribir un alcance global es simple. Define una clase que implemente la interfaz Illuminate\Database\Eloquent\Scope
. Esta interfaz requiere que implementes un método: apply
. El método apply
puede añadir restricciones where
a la consulta como sea necesario:
<?php namespace App\Scopes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; class AgeScope implements Scope { /** * Apply the scope to a given Eloquent query builder. * * @param \Illuminate\Database\Eloquent\Builder $builder * @param \Illuminate\Database\Eloquent\Model $model * @return void */ public function apply(Builder $builder, Model $model) { $builder->where('age', '>', 200); } }
Si tu scope global está agregando columnas a la cláusula select de la consulta, deberías usar el método addSelect
en lugar de select
. Esto evitará el reemplazo no intencional de la cláusula select existente de la consulta.
Aplicando scopes globales
Para asignar un scope global a un modelo, debes sobrescribir el método boot
del modelo dado y usar el método addGlobalScope
:
<?php namespace App; use App\Scopes\AgeScope; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * The "booting" method of the model. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope(new AgeScope); } }
Después de agregar el scope, una consulta a User::all()
producirá el siguiente código SQL:
select * from `users` where `age` > 200
Alcances globales anónimos
Eloquent también permite que definas scopes globales usando Closures, lo cual es particularmente útil para scopes simples que no se crean en una clase separada:
<?php namespace App; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * The "booting" method of the model. * * @return void */ protected static function boot() { parent::boot(); static::addGlobalScope('age', function (Builder $builder) { $builder->where('age', '>', 200); }); } }
Eliminar scopes globales
Si prefieres remover un scope global para una consulta dada, puedes usar el método withoutGlobalScope
. El método acepta el nombre de clase del scope global como su único argumento:
User::withoutGlobalScope(AgeScope::class)->get();
O, si definiste el scope global usando un Closure:
User::withoutGlobalScope('age')->get();
Si prefieres eliminar varios o incluso todos los scopes globales, puedes usar el método withoutGlobalScopes
:
// Remove all of the global scopes... User::withoutGlobalScopes()->get(); // Remove some of the global scopes... User::withoutGlobalScopes([ FirstScope::class, SecondScope::class ])->get();
Alcances (scopes) locales
Los scopes locales permiten que definas conjuntos de restricciones comunes que puedes reusar fácilmente a través de tu aplicación. Por ejemplo, puedes necesitar obtener frecuentemente todos los usuarios que son considerados «populares». Para definir un scope, agrega el prefijo scope
a un método de modelo de Eloquent.
Los scopes deberían devolver siempre una instancia del constructor de consultas:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * Scope a query to only include popular users. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopePopular($query) { return $query->where('votes', '>', 100); } /** * Scope a query to only include active users. * * @param \Illuminate\Database\Eloquent\Builder $query * @return \Illuminate\Database\Eloquent\Builder */ public function scopeActive($query) { return $query->where('active', 1); } }
Utilizando un scope local
Una vez que el scope ha sido definido, puedes ejecutar los métodos de scope al momento de consultar el modelo. Sin embargo, no debes incluir el prefijo scope
cuando ejecutas el método. Incluso puedes encadenar las ejecuciones a varios scopes, por ejemplo:
$users = App\User::popular()->active()->orderBy('created_at')->get();
La combinación de múltiples scopes de modelo Eloquent a través de un operador de consulta or
puede requerir el uso de funciones de retorno Closure como:
$users = App\User::popular()->orWhere(function (Builder $query) { $query->active(); })->get();
Sin embargo, dado que esto puede ser engorroso, Laravel proporciona un método de «orden superior» orWhere
que te permite encadenar estos scopes con fluidez sin el uso de Closure:
$users = App\User::popular()->orWhere->active()->get();
Scopes dinámicos
Algunas veces, puedes desear definir un scope que acepte parámetros. Para empezar, sólo agrega tus parámetros adicionales a tu scope. Los parámetros de scope deben ser definidos después del parámetro $query
:
<?php namespace App; use Illuminate\Database\Eloquent\Model; class User extends Model { /** * Scope a query to only include users of a given type. * * @param \Illuminate\Database\Eloquent\Builder $query * @param mixed $type * @return \Illuminate\Database\Eloquent\Builder */ public function scopeOfType($query, $type) { return $query->where('type', $type); } }
Ahora, puedes pasar los parámetros cuando llamas al scope:
$users = App\User::ofType('admin')->get();
Comparando modelos
En ocasiones necesitarás determinar si dos modelos son «el mismo». El método is
puede ser usado para verificar rápidamente que dos modelos tienen la misma llave principal, tabla y conexión a base de datos:
if ($post->is($anotherPost)) { // }
Eventos
Los modelos de Eloquent ejecutan varios eventos, permitiendo que captes los siguientes puntos en un ciclo de vida del modelo: retrieved
, creating
, created
, updating
, updated
, saving
, saved
, deleting
, deleted
, restoring
y restored
. Los eventos permiten que ejecutes fácilmente código cada vez que una clase de modelo específica es guardada o actualizada en la base de datos. Cada evento recibe la instancia del modelo a través de su constructor.
El evento retrieved
se disparará cuando un modelo existente es obtenido de la base de datos. Cuando un nuevo modelo es guardado la primera vez, los eventos creating
y created
se disparan. Si un modelo ya existe en la base de datos y el método save
es ejecutado, los eventos updating
/ updated
se dispararán. Sin embargo, en ambos casos, los eventos saving
/ saved
se dispararán.
Al realizar una actualización o eliminación masiva a través de Eloquent, los eventos de modelo saved
, updated
, deleting
y deleted
no se activarán para los modelos actualizados. Esto se debe a que los modelos nunca se recuperan cuando se emite una actualización o eliminación masiva.
Para empezar, define una propiedad $dispatchesEvents
en tu modelo Eloquent que mapee varios puntos del ciclo de vida de modelo de Eloquent a tus propias clases de eventos:
<?php namespace App; use App\Events\UserDeleted; use App\Events\UserSaved; use Illuminate\Foundation\Auth\User as Authenticatable; class User extends Authenticatable { use Notifiable; /** * The event map for the model. * * @var array */ protected $dispatchesEvents = [ 'saved' => UserSaved::class, 'deleted' => UserDeleted::class, ]; }
Después de definir y mapear tus eventos Eloquent, puedes usar listeners de eventos para manejar los eventos.
Observadores
Definiendo observadores
Si estás escuchando muchos eventos en un modelo dado, puedes usar observadores para agrupar todos tus listeners dentro de una sola clase. Las clases observadoras tienen nombres de métodos que reflejan los eventos de Eloquent que deseas escuchar. Cada uno de estos métodos recibe el modelo 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 nuevo observador lucirá como lo siguiente:
<?php namespace App\Observers; use App\User; class UserObserver { /** * Handle the User "created" event. * * @param \App\User $user * @return void */ public function created(User $user) { // } /** * Handle the User "updated" event. * * @param \App\User $user * @return void */ public function updated(User $user) { // } /** * Handle the User "deleted" event. * * @param \App\User $user * @return void */ public function deleted(User $user) { // } /** * Handle the User "forceDeleted" event. * * @param \App\User $user * @return void */ public function forceDeleted(User $user) { // } }
Para registrar un observador, usa el método observe
en el modelo que deseas observar. Puedes registrar los observadores en el método boot
de uno de tus proveedores de servicio. En este ejemplo, registraremos el observador en el AppServiceProvider
:
<?php namespace App\Providers; use App\Observers\UserObserver; use App\User; use Illuminate\Support\ServiceProvider; class AppServiceProvider extends ServiceProvider { /** * Register any application services. * * @return void */ public function register() { // } /** * Bootstrap any application services. * * @return void */ public function boot() { User::observe(UserObserver::class); } }
Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.
Lección anterior Redis - Documentación de Laravel 6 Lección siguiente Eloquent: Relaciones - Documentación de Laravel 6