Uso del modelo de base de datos (Estilo Laravel)
El modelo webman está basado en Eloquent ORM. Cada tabla de base de datos tiene un "modelo" correspondiente para interactuar con esa tabla. Puedes usar el modelo para consultar los datos en la tabla de datos y también para insertar nuevos registros en la tabla de datos.
Antes de comenzar, asegúrate de que la conexión a la base de datos esté configurada en config/database.php
.
Nota: Para que Eloquent ORM soporte observadores de modelos, se requiere importar adicionalmente
composer require "illuminate/events"
ejemplo.
Ejemplo de modelo de base de datos
<?php
namespace app\model;
use support\Model;
class User extends Model
{
/**
* Nombre de la tabla asociada al modelo
*
* @var string
*/
protected $table = 'user';
/**
* Redefinir la clave primaria, por defecto es id
*
* @var string
*/
protected $primaryKey = 'uid';
/**
* Indica si se deben mantener automáticamente las marcas de tiempo
*
* @var bool
*/
public $timestamps = false;
}
Nombre de la tabla
Puedes especificar una tabla de datos personalizada definiendo la propiedad table en el modelo:
class User extends Model
{
/**
* Nombre de la tabla asociada al modelo
*
* @var string
*/
protected $table = 'user';
}
Clave primaria
Eloquent también supone que cada tabla de datos tiene una columna de clave primaria llamada id. Puedes definir una propiedad $primaryKey protegida para sobrescribir esta convención.
class User extends Model
{
/**
* Redefinir la clave primaria, por defecto es id
*
* @var string
*/
protected $primaryKey = 'uid';
}
Eloquent supone que la clave primaria es un valor entero autoincremental, lo que significa que, de forma predeterminada, la clave primaria se convierte automáticamente en tipo int. Si deseas utilizar una clave primaria no autoincremental o que no sea numérica, debes establecer la propiedad pública $incrementing en false:
class User extends Model
{
/**
* Indica si la clave primaria del modelo es autoincremental
*
* @var bool
*/
public $incrementing = false;
}
Si tu clave primaria no es un entero, debes establecer la propiedad protegida $keyType en string en el modelo:
class User extends Model
{
/**
* "Tipo" del ID autoincremental.
*
* @var string
*/
protected $keyType = 'string';
}
Marcas de tiempo
De forma predeterminada, Eloquent espera que existan created_at y updated_at en tu tabla de datos. Si no deseas que Eloquent gestione automáticamente estas dos columnas, puedes establecer la propiedad $timestamps en false en el modelo:
class User extends Model
{
/**
* Indica si se deben mantener automáticamente las marcas de tiempo
*
* @var bool
*/
public $timestamps = false;
}
Si necesitas personalizar el formato de las marcas de tiempo, 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 el formato en el que se serializa el modelo a un arreglo o JSON:
class User extends Model
{
/**
* Formato de almacenamiento de las marcas de tiempo
*
* @var string
*/
protected $dateFormat = 'U';
}
Si necesitas personalizar los nombres de los campos para almacenar las marcas de tiempo, puedes establecer los valores de las constantes CREATED_AT y UPDATED_AT en el modelo:
class User extends Model
{
const CREATED_AT = 'creation_date';
const UPDATED_AT = 'last_update';
}
Conexión a la base de datos
Por defecto, los modelos Eloquent usarán la conexión a la base de datos predeterminada configurada en tu aplicación. Si deseas especificar una conexión diferente para el modelo, establece la propiedad $connection:
class User extends Model
{
/**
* Nombre de la conexión del modelo
*
* @var string
*/
protected $connection = 'connection-name';
}
Valores predeterminados de propiedades
Si deseas definir valores predeterminados para algunas propiedades del modelo, puedes definir la propiedad $attributes en el modelo:
class User extends Model
{
/**
* Valores predeterminados del modelo.
*
* @var array
*/
protected $attributes = [
'delayed' => false,
];
}
Recuperación de modelos
Una vez que hayas creado el modelo y la tabla de base de datos asociada, puedes consultar datos de la base de datos. Imagina cada modelo Eloquent como un poderoso constructor de consultas que te permite consultar más rápidamente la tabla de datos asociada. Por ejemplo:
$users = app\model\User::all();
foreach ($users as $user) {
echo $user->name;
}
Sugerencia: Dado que los modelos Eloquent también son constructores de consultas, también debes leer Constructores de consultas para conocer todos los métodos disponibles. Puedes utilizar estos métodos en las consultas Eloquent.
Restricciones adicionales
El método all de Eloquent devolverá todos los resultados del modelo. Dado que cada modelo Eloquent actúa como un constructor de consultas, también puedes añadir condiciones de consulta y luego utilizar el método get para obtener los resultados de la consulta:
$users = app\model\User::where('name', 'like', '%tom')
->orderBy('uid', 'desc')
->limit(10)
->get();
Recargar modelo
Puedes usar los métodos fresh y refresh para recargar un modelo. El método fresh volverá a recuperar el modelo de la base de datos. Las instancias de modelo existentes no se verán afectadas:
$user = app\model\User::where('name', 'tom')->first();
$fresh_user = $user->fresh();
El método refresh utiliza nuevos datos de la base de datos para volver a asignar el modelo existente. Además, las relaciones ya cargadas se recargarán:
$user = app\model\User::where('name', 'tom')->first();
$user->name = 'jerry';
$user = $user->fresh();
$user->name; // "tom"
Colección
Los métodos all y get de Eloquent pueden consultar múltiples resultados y devuelven una instancia de Illuminate\Database\Eloquent\Collection
. La clase Collection
proporciona una gran cantidad de funciones auxiliares para manejar los resultados de Eloquent:
$users = $users->reject(function ($user) {
return $user->disabled;
});
Uso de cursores
El método cursor te permite recorrer la base de datos usando cursores, solo ejecuta una consulta una vez. Al manejar grandes volúmenes de datos, el método cursor puede reducir drásticamente el uso de memoria:
foreach (app\model\User::where('sex', 1)->cursor() as $user) {
//
}
El cursor devuelve una instancia de Illuminate\Support\LazyCollection
. Lazy collections te permite utilizar la mayoría de los métodos de colección en Laravel, y cada vez solo se cargará un solo modelo en memoria:
$users = app\model\User::cursor()->filter(function ($user) {
return $user->id > 500;
});
foreach ($users as $user) {
echo $user->id;
}
Consultas de selección
Eloquent proporciona soporte avanzado para subconsultas, lo que te permite extraer información de tablas relacionadas con una única consulta. Por ejemplo, supongamos que tenemos una tabla de destinos llamada destinations y una tabla de vuelos llamada flights. La tabla flights contiene un campo arrival_at que indica cuándo llega el vuelo al destino.
Usando las funcionalidades de subconsulta proporcionadas por los métodos select y addSelect, podemos consultar en una sola declaración todos los destinos de destinations, junto con el nombre del último vuelo que llega a cada destino:
use app\model\Destination;
use app\model\Flight;
return Destination::addSelect(['last_flight' => Flight::select('name')
->whereColumn('destination_id', 'destinations.id')
->orderBy('arrived_at', 'desc')
->limit(1)
])->get();
Ordenar según subconsultas
Además, la función orderBy del constructor de consultas también admite subconsultas. Podemos usar esta función para ordenar todos los destinos según la hora de llegada del último vuelo a cada destino. También, esto puede ejecutarse como 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();
Recuperar modelo / colección única
Además de recuperar todos los registros de una tabla de datos específica, puedes usar los métodos find, first o firstWhere para recuperar un solo registro. Estos métodos devuelven una instancia de modelo único, en lugar de una colección de modelos:
// Buscar un modelo por la clave primaria...
$flight = app\model\Flight::find(1);
// Buscar el primer modelo que cumpla con la consulta...
$flight = app\model\Flight::where('active', 1)->first();
// Implementación rápida para buscar el primer modelo que cumpla con la consulta...
$flight = app\model\Flight::firstWhere('active', 1);
También puedes usar un arreglo de claves primarias como argumento al llamado al método find, lo que devolverá una colección de registros coincidentes:
$flights = app\model\Flight::find([1, 2, 3]);
A veces puedes desear realizar una acción diferente si no se encuentra un resultado al buscar el primero. El método firstOr realizará la búsqueda y, si se encuentra un resultado, devolverá el primero. Si no se encuentra, ejecutará el callback dado. El valor devuelto por el callback será el valor de retorno del método firstOr:
$model = app\model\Flight::where('legs', '>', 100)->firstOr(function () {
// ...
});
El método firstOr también acepta un arreglo de columnas para consultar:
$model = app\model\Flight::where('legs', '>', 100)
->firstOr(['id', 'legs'], function () {
// ...
});
Excepción de "no encontrado"
A veces deseas que se lance una excepción si no se encuentra un modelo. Esto es muy útil en controladores y rutas. Los métodos findOrFail y firstOrFail recuperan el primer resultado de la consulta y, si no se encuentra, lanzan una excepción de tipo Illuminate\Database\Eloquent\ModelNotFoundException:
$model = app\model\Flight::findOrFail(1);
$model = app\model\Flight::where('legs', '>', 100)->firstOrFail();
Recuperar colección
También puedes usar los métodos count, sum y max proporcionados por el constructor de consultas, así como otras funciones de colección para operar en la colección. Estos métodos solo devolverán el valor escalar adecuado y no una instancia de modelo:
$count = app\model\Flight::where('active', 1)->count();
$max = app\model\Flight::where('active', 1)->max('price');
Insercion
Para agregar un nuevo registro a la base de datos, primero debes crear una nueva instancia del modelo, establecer propiedades en la instancia y luego llamar al método save:
<?php
namespace app\controller;
use app\model\User;
use support\Request;
use support\Response;
class FooController
{
/**
* Agregar un nuevo registro en la tabla de usuarios
*
* @param Request $request
* @return Response
*/
public function store(Request $request)
{
// Validar solicitud
$user = new User;
$user->name = $request->get('name');
$user->save();
}
}
Las marcas de tiempo created_at y updated_at se establecerán automáticamente (cuando la propiedad $timestamps en el modelo sea true), por lo que no es necesario asignar valores manualmente.
Actualización
El método save también se puede utilizar para actualizar un modelo existente en la base de datos. Para actualizar un modelo, primero debes recuperarlo, establecer las propiedades que deseas actualizar y luego llamar al método save. De igual manera, la marca de tiempo updated_at se actualizará automáticamente, por lo que tampoco es necesario asignar el valor manualmente:
$user = app\model\User::find(1);
$user->name = 'jerry';
$user->save();
Actualización masiva
app\model\User::where('uid', '>', 10)
->update(['name' => 'tom']);
Verificar cambios de propiedad
Eloquent proporciona los métodos isDirty, isClean y wasChanged para verificar el estado interno del modelo y determinar cómo han cambiado sus propiedades desde que se cargó por primera vez.
El método isDirty determina si se ha cambiado alguna propiedad desde que se cargó el modelo. Puedes pasar un nombre de propiedad específico para determinar si una propiedad en particular ha cambiado. El método isClean es lo opuesto a isDirty y también acepta un parámetro de propiedad opcional:
$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 se ha modificado alguna propiedad durante el último ciclo de solicitud. También puedes pasar nombres de propiedad para ver si propiedades particulares han 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 nuevos modelos. Este método devuelve una instancia de modelo. Sin embargo, antes de usarlo, necesitas especificar las propiedades fillable o guarded en el modelo, ya que todos los modelos Eloquent son, por defecto, no asignables masivamente.
La vulnerabilidad de asignación masiva ocurre cuando un usuario pasa inesperadamente parámetros HTTP a través de una solicitud, y esos parámetros modifican campos en la base de datos que no deseas cambiar. Por ejemplo: un usuario malicioso podría pasar un parámetro is_admin a través de una solicitud HTTP y luego pasarlo al método create, lo que podría permitir al usuario actualizar su estado a administrador.
Por lo tanto, antes de comenzar, debes definir qué propiedades del modelo son asignables masivamente. Puedes hacerlo a través de la propiedad $fillable en el modelo. Por ejemplo, para permitir que la propiedad name del modelo Flight sea asignable de forma masiva:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* Propiedades que se pueden asignar masivamente.
*
* @var array
*/
protected $fillable = ['name'];
}
Una vez que hayamos establecido las propiedades que se pueden asignar masivamente, podemos insertar nuevos datos en la base de datos a través del método create. El método create devolverá la instancia del modelo guardado:
$flight = app\model\Flight::create(['name' => 'Flight 10']);
Si ya tienes una instancia de modelo, puedes pasar un arreglo al método fill para asignar valores:
$flight->fill(['name' => 'Flight 22']);
$fillable puede considerarse como una "lista blanca" para asignaciones masivas, y también puedes utilizar la propiedad $guarded para implementar esto. La propiedad $guarded consiste en un arreglo de propiedades que no se permitirán asignarse masivamente. Es decir, $guarded funcionará más como una "lista negra". Nota: solo puedes usar $fillable o $guarded, no ambos al mismo tiempo. En el siguiente ejemplo, todas las propiedades excepto price pueden asignarse masivamente:
<?php
namespace app\model;
use support\Model;
class Flight extends Model
{
/**
* Propiedades que no se pueden asignar masivamente.
*
* @var array
*/
protected $guarded = ['price'];
}
Si deseas que todas las propiedades sean asignables masivamente, puedes definir $guarded como un arreglo vacío:
/**
* Propiedades que no se pueden asignar masivamente.
*
* @var array
*/
protected $guarded = [];
Otros métodos de creación
firstOrCreate/ firstOrNew
Aquí hay dos métodos que podrías usar para la asignación masiva: firstOrCreate y firstOrNew. El método firstOrCreate intenta coincidir datos en la base de datos mediante el par clave/valor dado. Si no se encuentra el modelo en la base de datos, se insertará un nuevo registro que incluya las propiedades del primer argumento, así como las propiedades opcionales del segundo argumento.
El método firstOrNew intenta buscar un registro en la base de datos mediante las propiedades proporcionadas, de manera similar a firstOrCreate. Sin embargo, si el método firstOrNew no encuentra el modelo correspondiente, devolverá una nueva instancia del modelo. Ten en cuenta que la instancia del modelo devuelta por firstOrNew no se guarda aún en la base de datos, deberás llamar manualmente al método save para guardarla:
// Recuperar el vuelo por nombre, crear si no existe...
$flight = app\model\Flight::firstOrCreate(['name' => 'Flight 10']);
// Recuperar el vuelo por nombre, o crear usando el nombre y las propiedades delayed y arrival_time...
$flight = app\model\Flight::firstOrCreate(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
// Recuperar el vuelo por nombre, crear una instancia si no existe...
$flight = app\model\Flight::firstOrNew(['name' => 'Flight 10']);
// Recuperar el vuelo por nombre, o crear un modelo usando el nombre y las propiedades delayed y arrival_time...
$flight = app\model\Flight::firstOrNew(
['name' => 'Flight 10'],
['delayed' => 1, 'arrival_time' => '11:30']
);
También podrías enfrentarte a situaciones en las que deseas actualizar un modelo existente o crear uno nuevo si no existe. El método updateOrCreate logra esto en un solo paso. Similar al método firstOrCreate, updateOrCreate persiste el modelo, por lo que no es necesario llamar a save():
// Si existe un vuelo de Oakland a San Diego, su precio será 99 dólares.
// Si no se encuentra un modelo correspondiente, se creará uno.
$flight = app\model\Flight::updateOrCreate(
['departure' => 'Oakland', 'destination' => 'San Diego'],
['price' => 99, 'discounted' => 1]
);
Eliminar modelos
Puedes llamar al método delete en una instancia de modelo para eliminarla:
$flight = app\model\Flight::find(1);
$flight->delete();
Eliminar modelos mediante clave primaria
app\model\Flight::destroy(1);
app\model\Flight::destroy(1, 2, 3);
app\model\Flight::destroy([1, 2, 3]);
app\model\Flight::destroy(collect([1, 2, 3]));
Eliminar modelos mediante consulta
$deletedRows = app\model\Flight::where('active', 0)->delete();
Copiar modelos
Puedes usar el método replicate para copiar una nueva instancia que no se haya guardado en la base de datos. Este método es muy útil cuando las instancias de modelos comparten muchas propiedades similares.
$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();
Comparación de modelos
A veces puede ser necesario determinar si dos modelos son "iguales". El método is se puede utilizar para verificar rápidamente si dos modelos comparten la misma clave primaria, tabla y conexión a la base de datos:
if ($post->is($anotherPost)) {
//
}
Observadores de modelos
Referencia a Eventos de modelo y Observador en Laravel
Nota: Para que Eloquent ORM soporte observadores de modelos, se requiere importar composer require "illuminate/events"
<?php
namespace app\model;
use support\Model;
use app\observer\UserObserver;
class User extends Model
{
public static function boot()
{
parent::boot();
static::observe(UserObserver::class);
}
}
Transacciones
Consulta Transacciones de base de datos