Bienvenidos a una nueva entrega de Eloquent con ejemplos. Hasta ahora hemos aprendido sobre diversas funciones y herramientas que este genial ORM nos ofrece, sin embargo, no hemos visto como usarlo para persistir nuestros datos.
Al principio de la serie, vimos las diferencias entre hacer un insert()
y un create()
, como recordaremos, el primero ejecuta una instrucción sql que coloca los datos directamente dentro de nuestra tabla, mientras que el segundo crea una instancia de un modelo eloquent, lo que nos resulta bastante útil al momento de querer seguir usando esta información para ejecutar alguna otra lógica dentro de nuestra aplicación.
Cosas que aprenderemos:
- Create y Update
- Atributos fillable y guard
- findOrNew
- firstOrNew/firstOrCreate
- updateOrCreate
Create y Update
En lecciones anteriores aprendimos como usar el QueryBuilder para insertar un nuevo registro en la base de datos, a través del uso de la facade DB
:
DB::table('dogs')->insert(['name' => 'Old Yeller', 'age' => 12]); // result "true"
Como bien lo mencionamos más arriba, esto no hace más que ejecutar una instrucción sql para persistir los datos que le enviamos en la tabla correspondiente, recibiendo una respuesta booleana que nos indica si su ejecución fue exitoso o no, y eso es todo, esta instrucción no agregará marcas de tiempo ni hará alguna otra cosa por nosotros, si necesitáramos seguir trabajando con esta data que enviamos, pues habría que ejecutar una consulta y traer el registro, o trabajar con la información en crudo tal cual la enviamos. Probemos ahora con Eloquent a ver que tal nos va y si nos puede ayudar con esta tarea:
$dog = Dog(); // result "App\Models\Dog"
$dog->name = 'Just-right Yeller'; // result "Just-right Yeller"
$dog->age = 6; // result 6
$dog->save(); // result "true"
En este ejemplo, estamos haciendo uso del método save()
que proporciona eloquent, para ello, debemos asignar los valores a cada propiedad de nuestro modelo y ejecutar el método, esto actuará de la misma manera que el insert
de arriba, persistiendo nuestros datos, pero con el plus de tener una instancia de nuestro modelo, lo que nos permitirá trabajar con este registro de una manera más cómoda, pudiendo hacer casi cualquier cosa con ella. Sin embargo, podemos ver que acá trabajamos con una especie de enfoque híbrido, hemos creado una instancia del modelo, luego establecimos los atributos, para finalmente guardarlo, a grandes rasgos podríamos decir que fueron tres pasos ejecutados, veamos una tercera forma de guardar nuestra información con menos instrucciones:
Dog::create(['name' => 'Young Yeller', 'age' => 3]);
Nos ha reventado en la cara esta exception
: "Illuminate\Database\Eloquent\MassAssignmentException con el mensaje 'name'" - ¿MassAssignmentException, de qué se trata?
Pues bien, aquí es donde nuestros amigos $fillable
y $guard
entran al juego. Eloquent, en su grandiosa misión de facilitarnos el manejo de información desde y hacia la base de datos, no solo nos proporciona métodos y propiedades para este particular, además, no brinda apoyo en cuanto a la protección en la ejecución de estas tareas, al garantizar que solo se inserten o actualicen los campos que se desean rellenar y nada más, para ello, en primer lugar tenemos a la propiedad $fillable
, la cual es una especie de lista blanca, en la cual le indicamos cuales son los campos de nuestra base de datos que puede guardar a través de la asignación en masa. En segundo lugar, tenemos $guard
la cual es todo lo contrario, en ella indicamos aquellos que deseamos proteger y que solo podrán ser asignados de manera directa (como en el segundo ejemplo usando save()).
Solucionemos esto y pongámoslo en marcha. Vayamos al modelo Dog y agreguemos la siguiente línea:
protected $fillable = ['name'];
Ahora desde tinker creemos un nuevo registro:
Dog::create(['name' => 'Young Yeller', 'age' => 3]);
Con esta instrucción hemos hecho lo mismo que en el ejemplo anterior, salvo una excepción, hemos cometido un error y no nos hemos dado cuenta. Si revisamos nuestra base de datos, encontraremos que efectivamente nuestro amigo Young Yeller se encuentra registrado, sin embargo, a pesar que hemos indicado su edad al momento del registro, este dato no ha persistido, ¿y por qué?, pues tan simple como que no le indicamos a $fillable
que podía permitir la asignación de este campo. Este es un error muy común y de los que muchas veces tardamos en darnos cuenta debido a que en realidad no existe ninguna señal de error que así no los indique, por lo que podríamos pasar horas quebrándonos la cabeza buscando el motivo por el cual nuestros datos no persisten, muchos podrán pensar que esto es una desventaja e incluso algunos tratarían de evitar el uso de este método, sin embargo, debemos tomar en cuenta que esto es así por una razón, la ‘inyección de SQL’, de esta manera, aunque cualquier atacante intente y logre agregar campos no definidos por nosotros, estos no afectarán a nuestra aplicación puesto que serán ignorados por Eloquent.
La inyección de sql es un tipo de ataque muy común en la web, si quieres saber un poco más sobre ello, puedes hacerlo en el link: Inyección de SQL
Ahora bien, aunque hasta ahora hemos visto tres diferentes maneras de como guardar nuestros datos, aún quedan algunos otros métodos que nos ayudan en esta misma tarea, y si bien puede parecer abrumador tener que aprender tantas opciones y manera de hacer una cosa, la verdad es que cada una tiene una finalidad o ayudan de una manera distinta, solventando tipos de situaciones diferentes.
Vemos entonces una nueva forma de hacer lo anterior:
$dog = new Dog (['name' => 'Young Yeller', 'age' => 3]); // result App\Models\Dog {#690 name: "Young Yell
Como vemos, la instrucción anterior no hace más que crear una instancia del modelo Dog, mas no persiste ni crea ningún registro, si queremos que esto suceda debemos invocar al método save()
desde nuestra instancia.
findOrNew, firstOrNew/firstOrCreate:
Estas tres funciones son increíblemente útiles, sin embargo, muchos desarrolladores de Laravel rehúyen de ellas como algo desconocido. No hay necesidad de hacerlo; de hecho, a estas alturas ya deberíamos tener algún entendimiento de lo que hacen.
En los ejemplos anteriores hemos visto como al hacer uso de new
solo creamos una instancia del modelo, nada distinto a una simple creación de instancia desde php puro $dog = new Dog($data)
. Por su parte, Create
también crea una instancia del modelo con la distinción de que además persiste en la base de datos la información contenida en la instancia, o lo que se podría traducir en $dog= (new Dog($data))->save()
, en este sentido, podemos decir que:
- findOrNew lo que hace es “Buscar un registro con la clave principal, si la encuentra la devuelve y si no crea una instancia nueva y vacía del modelo.
>>> $dog = Dog::findOrNew(100)
=> App\Models\Dog {#4061}
- firstOrNew lo que hace es “ Buscar el primer registro que cumpla con la condición. Si no puede encontrar uno, crea una nueva instancia de modelo poblada con la información dada sin persistirla"
// Si existe lo devuelve
>>> $dog = Dog::firstOrNew(['name' => 'Jock'])
=> App\Models\Dog {#4284
id: 2,
name: "Jock",
gender: "male",
age: 7,
created_at: "2021-06-07 02:05:46",
updated_at: "2021-06-07 02:05:46",
deleted_at: null,
}
// Si no existe crea la instancia
>>> $dog = Dog::firstOrNew(['name' => 'Champ'])
=> App\Models\Dog {#3333
name: "Champ",
}
- firstOrCreate lo que hace es “ Buscar el primer registro que cumpla con la condición. Si no puede encontrar uno, crea una nueva instancia de modelo poblada con la información dada y la guarda en la base de datos"
>>> $dog = Dog::firstOrCreate(['name' => 'Champ'])
=> App\Models\Dog {#4282
name: "Champ",
updated_at: "2021-06-27 15:26:41",
created_at: "2021-06-27 15:26:41",
id: 6,
}
>>> Dog::find(6)
=> App\Models\Dog {#4287
id: 6,
name: "Champ",
gender: null,
age: null,
created_at: "2021-06-27 15:26:41",
updated_at: "2021-06-27 15:26:41",
deleted_at: null,
}
Ya los vamos entiendo, sin embargo, quizás surja la duda de ¿Por qué querríamos hacer instancias vacías? Hay algunas razones, pero una es que cuando buscamos un registro, siempre tendremos una instancia de modelo como clase de resultado, incluso si no se encuentra la que queremos, esto significa que podemos escribir nuestro código de forma más coherente y limpia, porque no tenemos que ensuciarlo con muchos condicionales por todas partes.
updateOrCreate:
Antes de terminar por hoy, veamos una última función a tener en cuenta updateOrCreate()
. Esta función nos permite concluir una búsqueda de varias partes con una instrucción de inserción o de actualización dependiendo de su resultado. Por ejemplo:
Dog::updateOrCreate(['id' => 1, 'name' => 'Joe'], ['age' => 15]);
En ese caso, al igual que los anteriores, buscará un perro cuyo id se igual a 1 y su nombre sea Joe, ambas condiciones son incluyente, al encontrar una coincidencia, cambiará su edad al valor de 15, por otro lado:
Dog::updateOrCreate(['id' => 2, 'name' => 'Joe'], ['age' => 15]);
Al no existir ninguna coincidencia que satisfaga las condiciones, esta función procederá a crear un nuevo registro con la información dada, asignando el nombre Joe y la edad de 15 al id correspondiente al nuevo registro. Como vemos, esto es útil para cosas como un perfil de usuario, donde es posible que no deseemos pedirle al usuario que complete una gran cantidad de información cuando se registre, pero si mostrarle una pantalla de perfil donde pueda terminar de actualizar más tarde.
Hoy hemos visto un montón de técnicas distintas, pero a la vez muy fácil de entender e implementar, además de tener un montón de ventajas como Eloquent ya nos tiene acostumbrados, sin embargo, es importante que nos tomemos el tiempo para experimentar con cada una de ellas; ver que funciona como esperamos y lo que no, y por supuesto, tratar de hacer cualquier variación que se nos ocurra. Hay muchos más trucos que podemos usar con lo aprendido hoy, solo debemos profundizar más en nuestro aprendizaje, echarle un ojo a la documentación oficial y al mismo código de Laravel y ser creativos.
Es todo por el día de hoy, quédate atento a la próxima entrega, si tienes alguna duda puedes contactarme en mi cuenta de twitter @johantovar o déjala en los comentarios. Hasta entonces y que tengas un feliz y exitoso día.
Repositorio de práctica: jtovarto/serie-eloquent-con-ejemplo
Top comments (4)
Muy buenos cada uno de los Post !!!! Tremendosss !!!!!Gracias por compartirlos
Muchas gracias pro el comentario, espero sea de ayuda, muy ponto estare de regreso y terminare la serie, saludos!
Muy buenos artículos!!
Gracias por el comentario Angel. Todavía quedan algunas lecciones, espero te sirvan de ayuda. Saludos!!