Índice de contenido
- ¿Qué es Eloquent y ORM?
- Operaciones CRUD con Eloquent sobre los modelos
- Crear un registro
- Actualizar un registro
- Inyección de Modelos (Route Model Binding)
- Obtener registros
- get() y all()
- first()
- Consultas personalizadas con where()
- El Mapeo del ORM
- Eliminar un registro
- Atributos de Clase: La Nueva Sintaxis a partir de Laravel 13
- Cómo implementar el Atributo Fillable
- Ejemplo de implementación:
- ¿Cuál debería usar?
- Verificando el funcionamiento
- Operaciones comunes en Eloquent (ORM)
- Ver el SQL
- Joins
- Ordenación
- Where o orWhere anidados
- WhereIn y WhereNotInt
- Obtener un registro
- Limitar la cantidad de registros
- Cantidad
- Obtener registros aleatorios
- Serialización
- Restricciones de consulta
- Limit y Offset
- pluck() vs modelKeys(): cómo obtener un array de IDs de una colección Eloquent
- El problema común: obtener un array de IDs en Laravel
- Trabajando con relaciones de Eloquent (ejemplo hasMany)
- Uso de pluck() para extraer IDs de una Colección
- Uso básico de pluck('id')
- Cuándo pluck() es la opción más segura
- Formateo de valores con cierres (closures) en pluck()
- Uso de modelKeys(): qué hace realmente
- Cómo funciona modelKeys() internamente
- Claves primarias y nombres de clave no estándar
- Por qué modelKeys() solo funciona en Colecciones
- pluck() vs modelKeys(): diferencias clave
- Flexibilidad vs simplicidad
- Consideraciones de rendimiento
- Errores comunes y conceptos erróneos
- Errores comunes al extraer valores de modelos de Eloquent
- Por qué only() no funciona en los atributos del modelo
- Colección vs Modelo: el modelo mental
- ¿Cuál deberías usar en proyectos reales?
- Reglas de decisión claras
- Lo que realmente uso la mayor parte del tiempo
- Preguntas frecuentes
- Conclusión
- Métodos Model Casts a partir de Laravel 11
En Laravel, todo es una clase. La importancia de los modelos radica en que heredan de la clase Model, lo que les otorga automáticamente la capacidad de interactuar con la base de datos.
A la final, es tomar nuestro modelo, que hereda de Model, lo que significa, que es una clase modelo y con ella tenemos de gratis múltiples funciones que podemos hacer para comunicarnos con la base de datos como las que vimos anteriormente.
Por convención, Laravel hace un "match" automático: si tu modelo se llama Post (en singular), él entiende que debe conectarse a la tabla posts (en plural). No necesitas configurar nada más; la magia de la herencia ya te da métodos para crear, leer, actualizar y borrar (CRUD) de forma gratuita.
Es importante mencionar que, estas son solamente algunas operaciones (las que vamos a ver), las más comunes, pero tenemos muchísimas más:
https://laravel.com/docs/master/eloquent
¿Qué es Eloquent y ORM?
Para interactuar con los datos, Laravel utiliza Eloquent. Este es un ORM (Object-Relational Mapping).
- Object (Objeto): Porque trabajamos con clases y objetos PHP.
- Relational (Relacional): Porque nuestras bases de datos son relacionales (como SQLite o MySQL).
- Mapping (Mapeo): Porque Eloquent "mapea" o traduce cada fila de tu tabla a un objeto de PHP.
Así, en lugar de escribir SQL complejo, simplemente usas métodos como Post::all() para obtener todo o Post::create() para insertar. Los nombres en Laravel (Eloquent, Blade, Artisan) suenan casi como superhéroes, ¡y realmente lo son para nuestra productividad!
También recuerda que, los modelos son la única capa que contamos para comunicarnos con la base de datos; lo cual lo hace excelente, ya que, con esto podemos hacer independiente la base de datos con la cual estemos trabajando, por ejemplo, MySQL, MariaDB, PSQL, SQL Server… del proyecto; podemos cambiar de una base de datos a otra fácilmente y con pocas configuraciones.
En este apartado, vamos a ver las operaciones de tipo CRUD en la base de datos mediante los modelos.
Operaciones CRUD con Eloquent sobre los modelos
Veamos como realizar las operaciones CRUD sobre los modelos, en este caso, un modelo de Post.
Crear un registro
Finalmente, si queremos crear un post:
public function index()
{
return Post::create(
['title' => "test",
'slug' => "test",
'content' => "test",
'category_id' => 1,
'description' => "test",
'posted' => "not",
'image' => "test"]
);
}Es importante que la categoría con ID de 1 exista; si no, verás un error como el siguiente:
SQLSTATE[23000]: Integrity constraint violation: 1452 Cannot add or update a child row: a foreign key constraint fails (`testlara10`.`posts`, CONSTRAINT `posts_category_id_foreign` FOREIGN KEY (`category_id`) REFERENCES `categories` (`id`)Puedes crear la categoría desde la base de datos.
Si vamos al navegador:
http://larafirststeps.test/post
Verás que, ocurre un error:
Add [title] to fillable property to allow mass assignment on [App\Models\Post].En primera instancia, el error puede parecer extraño, pero lo que está ocurriendo es que, falta definir una configuración extra sobre nuestro modelo; debemos de decirle a Laravel que columnas son las que podrás ser gestionadas para nuestros posts; es decir, crear o actualizar; esto es particularmente útil ya que, puedes tener columnas que no quieres que sean manipuladas por el framework, que manejan información delicada, para hacer debug, de control, o seguridad.
Suponte que tienes un modelo de usuarios con una columna de role, cuyo role puede ser o administrador o regular, administrador en la aplicación solamente hay uno, que es el que creas directamente en la base de datos; por lo tanto, la columna de los roles no vas a querer que sea administrada por el framework y esto es, para evitar posibles vulnerabilidades en la cual un usuario malicioso pueda explotar alguna vulnerabilidad de la aplicación en la cual se cree o administre un usuario y pueda cambiar el role del usuario por dicho medio; esto, por dar un ejemplo.
Volviendo a los campos o columnas administrables, para definirlas tenemos que definir una propiedad protegida llamada fillable en la base de datos, que no es más que un array que define los campos que son “rellenables”:
protected $fillable = ['title', 'slug', 'content', 'category_id', 'description', 'posted', 'image'];Ya con esto si ingresamos nuevamente a la página:
{
"title": "test",
"slug": "test",
"content": "test",
"category_id": 1,
"description": "test",
"posted": "not",
"image": "test",
"updated_at": "2026-03-02T19:43:42.000000Z",
"created_at": "2026-03-02T19:43:42.000000Z",
"id": 1
}Veremos que se creó correctamente; desde el navegador, veremos un error como el siguiente:
App\Http\Controllers\Dashboard\PostController::index(): Return value must be of type Illuminate\Http\Response, App\Models\Post returnedY esto es por el tipo que debemos de devolver; siguiendo el esquema de cliente/servidor el cliente, desde el navegador, realiza una consulta a nuestra aplicación, en donde, la consulta es procesada por un controlador; la consulta, es el request; por ejemplo:
use Illuminate\Http\Request;
public function store(Request $request): {
}Ahora, si intentamos crear el post, veremos un error por la FK:
SQLSTATE[23000]: Integrity constraint violation: 19 FOREIGN KEY constraint failed (Connection: sqlite, Database:Y es que, primero debemos de crear la categoría y crear los campos filleable como hicimos con los posts:
Category::create([
'title' => 'Cate 1',
'slug' => 'cate-1'
]);El siguiente paso, es que el servidor, devuelva una respuesta lo cual es hecho desde el mismo controlador que es el que recibe la petición; esta respuesta, en Laravel es del tipo:
use Illuminate\Http\Response;
class PostController extends Controller
{
public function index(): Response
{
}
***
}Lo cual es diferente a la operación que estamos retornando que, en el ejemplo anterior, es un post; y por eso la excepción.
Actualizar un registro
A diferencia de la creación, donde usamos un método estático (Post::create) porque el objeto aún no existe, para actualizar trabajamos sobre una instancia ya existente.
Para actualizar un post, basta con emplear el método update() sobre el post que queremos editar e indicamos los campos:
public function index()
{
$post = Post::find(1);
return $post->update(
[
'title' => "test new",
'slug' => "test",
'content' => "test",
'category_id' => 1,
'description' => "test",
'posted' => "not",
'image' => "test"
]
);
}Si revisamos en la base de datos, verás que se actualizó el registro.
Si quieres practicar con las rutas, para el pase de parámetro en la ruta usando corchetes []:
Route::get('/post/{post}/edit', [PostController::class, 'edit']);Inyección de Modelos (Route Model Binding)
Laravel es increíblemente inteligente: si en tu controlador tipas el parámetro con el nombre del modelo, el framework hará el trabajo sucio por ti.
- Sin inyección: Recibirías un número y tendrías que hacer Post::findOrFail($id).
public function edit(int $post)
- Con inyección: Laravel busca el registro automáticamente. Si el ID no existe, devuelve un error 404 de inmediato; si existe, te entrega el objeto $post listo para usar.
public function edit(Post $post)
Obtener registros
Veamos los métodos más comunes que podemos usar obtener datos, ya sea una colección o array o una sola instancia.
get() y all()
Para obtener todos los registros:
public function index()
{
return Post::get();
}Para listar todo lo que hay en una tabla, el método más directo es all(), aunque también solemos usar get() cuando aplicamos filtros previos.
$categories = Category::all();
dd($categories); // Dump and Die: Inspecciona los datos y detiene la ejecuciónEl método dd() es tu mejor amigo. Es como un print o echo, pero con esteroides: muestra los datos con colores, permite colapsar objetos y detiene el código para que no se ejecuten errores posteriores (como divisiones por cero).
En este caso para que puedas apreciar el formato devuelto la recomendación es que tengas al menos dos posts en la base de datos; basta con ejecutar un par de veces más el código que definimos en el apartado de crear un post:
[
{
"id": 1,
"title": "test new",
"slug": "test",
****
"created_at": "2026-02-26T21:35:59.000000Z",
"updated_at": "2026-03-02T19:47:18.000000Z",
"category_id": 1
},
{
"id": 2,
"title": "test",
"slug": "test",
"created_at": "2026-02-23T15:19:35.000000Z",
"updated_at": "2026-02-26T18:11:42.000000Z",
"category_id": 1
},
{
****
]Para obtener un solo post, usamos el método de find() la cual recibe el id del elemento que queremos encontrar:
public function index()
{
return Post::find(1);
}Es la forma corta de buscar un registro por su Primary Key (ID).
- Acepta un entero o un string (en caso de que uses UUIDs).
- Si el ID existe, te devuelve el objeto; si no, devuelve null.
first()
A diferencia de get(), que devuelve una colección (array), first() devuelve únicamente el primer objeto que coincida con la consulta. Es ideal cuando sabes que solo necesitas un resultado.
Consultas personalizadas con where()
¿Qué pasa si no quieres buscar por ID, sino por el título o el slug? Aquí es donde usamos la cláusula where(). Eloquent traducirá esto automáticamente a SQL.
// SQL equivalente: SELECT * FROM categories WHERE title = 'KT1' LIMIT 1;
$category = Category::where('title', 'KT1')->first();El Mapeo del ORM
Aquí es donde el término ORM (Object-Relational Mapping) cobra todo el sentido:
- Mapeo: Laravel traduce una fila de tu tabla (Relacional) a una instancia de una clase PHP (Objeto).
- Singular vs Plural: Por eso el modelo se llama Category (en singular), porque cada objeto representa una fila. Cuando obtienes muchos, Laravel te entrega un "Array" o Colección de esos objetos singulares.
Eliminar un registro
Para eliminar algo, al igual que para actualizarlo, primero tiene que existir. Es una cuestión de lógica aplicada al mundo real: no puedes aumentar la memoria de un teléfono que no tienes, ni puedes tirar a la basura un bizcocho que no has horneado. Si quieres el bizcocho, lo creas; si te comes un pedazo, lo actualizas (modificas su estado); y si se daña, lo eliminas.
En Laravel, esta relación con el mundo real se mantiene. Para eliminar un registro, el framework realiza un find implícito mediante la Primary Key (PK). Si el objeto existe, procedemos a borrarlo; si no, no hay nada que destruir.
Finalmente, para eliminar un post, dado el post, lo eliminamos con el método de delete().
public function index()
{
$post = Post::find(1);
return $post->delete();
}Es importante la coherencia en los nombres. Si en tu ruta defines el parámetro como {post}, en tu método debes recibirlo como $post. Laravel utiliza el Route Model Binding para buscar el registro automáticamente:
public function destroy(Post $post)- Si el registro existe: Laravel inyecta el objeto en el método y continúa la ejecución.
- Si el registro no existe: Laravel devuelve un error 404 inmediatamente y ni siquiera llega a ejecutar el código dentro del método (por eso, si tuvieras un error de programación como una división entre cero, no llegaría a explotar si el ID es inválido).
Atributos de Clase: La Nueva Sintaxis a partir de Laravel 13
Si observas con detenimiento el modelo User que viene por defecto, notarás que ya no utiliza exclusivamente las propiedades tradicionales como protected $fillable. En su lugar, aparece una sintaxis basada en Atributos de PHP (esos corchetes #[...] encima de la clase).
A partir de Laravel 13, el framework permite definir la configuración de los modelos como atributos de clase. Esto es una alternativa moderna a la forma clásica, y tú puedes decidir cuál utilizar según te sientas más cómodo.
Cómo implementar el Atributo Fillable
Si quieres migrar de la forma clásica a esta nueva sintaxis, debes asegurarte de importar el namespace correcto, ya que es un error común confundirlo con otros similares.
Ejemplo de implementación:
Primero, importa el atributo y luego decláralo sobre la clase:
app/Models/Category.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Model;
#[Fillable(['title', 'slug'])]
class Category extends Model
{
// protected $fillable = ['title', 'slug'];
function posts() {
return $this->hasMany(Post::class);
}
}¿Cuál debería usar?
Ambas formas funcionan exactamente igual bajo el capó. Sin embargo, aquí te doy un consejo de peer:
- Sintaxis Clásica (protected $fillable): Es la que encontrarás en el 99% de la documentación actual y tutoriales en internet. Si usas herramientas de IA para ayudarte, probablemente te responderán con este formato porque es el estándar histórico.
- Sintaxis de Atributos: Es más limpia visualmente y sigue las tendencias más recientes de PHP.
Verificando el funcionamiento
Para confirmar que esta nueva sintaxis funciona, podemos forzar un error. Si intentamos crear un registro omitiendo un campo obligatorio (como el slug) que hemos definido en el atributo, Laravel lanzará la misma excepción de violación de integridad que veíamos antes.
Esto demuestra que el atributo está cumpliendo su función de "filtrar" qué campos pueden ser insertados masivamente.
Operaciones comunes en Eloquent (ORM)
Estos son algunos de los querys builder que yo he empleado en mis proyectos en Laravel, y quiero traer algunos para que tengas una referencia un poco más completa de lo que puedes hacer; nuevamente el propósito de estos query builders, es que puedes conocer cómo realizar operaciones que en algún momento vas a necesitar y su propósito en el siguiente capítulo es netamente referencial.
Como recomendación, prueba estos queréis, ver el SQL generado mediante el método de toSql() que ya vamos a evaluar.
En Laravel, el ORM que empleamos se llama Eloquent y está ligado a los modelos, por ejemplo:
Post::where('posts.id',$id);https://laravel.com/docs/master/eloquent
Pero, también podemos emplear consultas sin emplear modelos, empleando el Query Builder:
DB::table('posts')->where('posts.id',$id);Ambos esquemas permiten emplear la mayoría de los métodos y son equivalentes para la mayoría de los ejemplos que vamos a ver en este apartado:
https://laravel.com/docs/master/queries
Para estos ejercicios, te puedes apoyar en Tinker, para probar las consultas desde la línea de comandos:
$ php artisan tinkerVer el SQL
Si quieres ver el SQL generado de una consulta, sin importarnos que complejo sea, en vez de indicar el método de get(), find(), first() o cualquier otra para obtener los datos, sustitúyelo con el método de:
DB::table('posts')->toSql();
// "select * from "posts""
DB::table('posts')->where('id','>',5)->toSql()
// "select * from "posts" where "id" > ?"O en Eloquent:
$post = Post::where('id','>',5)->toSql()
// "select * from "posts" where "id" > ?"Joins
Los joins, son una estructura de lo más útil con la cual podemos combinar distintos campos referenciados por un campo en común; usualmente la clave foránea:
Post::join('categories', 'categories.id', '=', 'posts.category_id')->
select('posts.*', 'categories.title as category')->
orderBy('posts.created_at', 'desc')->paginate(10);
Post::join('categories','categories.id','=','posts.category_id')->select('posts.*','categories.id as c_id')->get() En el ejemplo anterior, puedes ver que de manera demostrativa, se emplean otros métodos como el de select() u orderBy(); ya que, usualmente para realizar las consultas, se emplean varios de estos métodos dependiendo de lo que queramos realizar, usualmente podemos colocar estos métodos en cualquier parte de la consulta (salvo si estamos agrupando) pero deben de estar antes de los métodos que resuelven el resultado, es decir, antes del get(), find() o similares.
Recuerda que tenemos distintos tipos de joins, y en Laravel, podemos emplear cada uno de ellos:
https://laravel.com/docs/master/queries#joins
Ordenación
Si no quieres ordenar por el id, que es el valor por defecto, puedes ordenar por una columna en particular:
Post::join('categories', 'categories.id', '=', 'posts.category_id')->
select('posts.*', 'categories.title as category')->
orderBy('posts.created_at', 'desc')->paginate(10);Where o orWhere anidados
En Laravel, tenemos todos los tipos de where; pero, sabemos que cuando empleamos un where y un orWhere al mismo nivel sin agruparlos, lo devuelto, no es lo que esperamos; para poder agrupar mediante un orWhere o similares:
$posts = Post::join('categories', 'categories.id', '=', 'posts.category_id')
->select('posts.*', 'categories.title as category', 'categories.slug as c_slug')
->where('categories.slug', $category_slug)
->where('posted', "yes")
->where(function ($query) {
$query->orWhere('type', 'post')
->orWhere('type', 'courses')
->orWhere('type', 'group');
})
->orderBy('posts.created_at', 'desc')
->paginate(10);Si quisiéramos pasar parametros, puedes emplear el método de use como puedes ver en el siguiente ejemplo:
$category_id = 1;
Post::where('id', '>=', 1)->where(function ($query) use ($category_id) {
dd($category_id);
$query->where('category_id', $category_id)->orWhere('posted', 'yes');
})->get();WhereIn y WhereNotInt
También tenemos el método para buscar por un array de ids:
$ids = array( 1, 2, 3, 4, 5, 6 );
$posts = Post::whereIn('posts.id',$ids);
$posts = Post::whereNotIn('posts.id',$ids);Obtener un registro
Sin importar la consulta, si solamente quieres obtener un solo registro en el resultado:
$posts = Post::where('slug', $slug)->first();Limitar la cantidad de registros
Si quieres limitar la cantidad de registros:
$posts = Post::limit(3)->get();También podemos indicar un bloque o página en específico:
$posts = Post::limit(3)->offset(2)->get() Ambas funciones son útiles para crear una paginación personalizada.
Cantidad
Para obtener la cantidad de registros, podemos emplear el método de count():
Post::limit(2)->offset(2)->get()->count() Obtener registros aleatorios
Si quieres obtener registros de manera aleatoria:
$posts = Post::where(<>)->inRandomOrder()->get();Serialización
La serialización se refiere al proceso de convertir objetos o estructuras de datos a un formato en particular para poder ser más fácilmente transmitidos y con esto consumidos, el formato JSON es un buen ejemplo de esto, que es el formato por excelencia empleado al momento de crear una Rest Api como veremos más adelante.
Mediante Eloquent, podemos convertir los datos obtenidos mediante una consulta a formato JSON:
$post = Post::find(1);
$json = $post->toJson();O array:
$post = Post::find(1);
$array = $post->toArray();Restricciones de consulta
Con Eloquent, podemos realizar toda clase de restricciones que acompañan la consulta y con esto poder recuperar datos específicos, entre las principales tenemos:
- "where": agrega una cláusula básica where a la consulta.
- "orWhere": agrega una cláusula "or" where a la consulta.
- "whereIn": agrega una cláusula where in a la consulta.
- "whereBetween": agrega una cláusula where between a la consulta.
- "orderBy": ordena los resultados de la consulta por una columna especificada.
- "limit": limita el número de registros devueltos por la consulta.
- "offset": Especifica un DESPLAZAMIENTO desde donde comienza a devolver los datos.
Limit y Offset
Puede utilizar los métodos skip y take para limitar la cantidad de resultados devueltos por la consulta o para omitir una cantidad determinada de resultados en la consulta:
$posts = Post::skip(10)->take(5)->get();Alternativamente, puede utilizar los métodos de límite y compensación. Estos métodos son funcionalmente equivalentes a los métodos tomar y omitir, respectivamente:
$posts = Post::offset(10)->limit(5)->get();Todas estas restricciones se pueden emplear en conjunto como hemos visto en los ejemplos anteriores:
$users = Post::where('type', 'post')
->orWhere('type', 'book')
->orderBy('created_at', 'desc')
->limit(10)
->get ();pluck() vs modelKeys(): cómo obtener un array de IDs de una colección Eloquent
Hay muchas situaciones en Laravel donde simplemente necesitas un array de IDs de un conjunto de modelos. Nada sofisticado: sin campos extra, sin transformaciones… solo los identificadores.
Suena sencillo, pero una vez que empiezas a trabajar con relaciones de Eloquent, claves primarias no estándar o tablas grandes, la decisión entre pluck() y modelKeys() en realidad importa más de lo que parece.
Me he topado con este mismo problema muchas veces, así que vamos a desglosarlo adecuadamente.
El problema común: obtener un array de IDs en Laravel
Un escenario muy típico se ve así:
- Tienes una relación
hasMany - Un modelo ya cargado
- Necesitas los IDs de los modelos relacionados
Por ejemplo, un Role que tiene muchos modelos Permission.
En ese punto, usualmente ya tienes una Colección de Eloquent, no un constructor de consultas (query builder) — y ese detalle es clave.
Trabajando con relaciones de Eloquent (ejemplo hasMany)
$permissionIDs = $role->permissions->pluck('id'); Esto funciona, es legible y la mayoría de los desarrolladores de Laravel lo usan por instinto. Y honestamente, en proyectos reales, esto sigue siendo lo que utilizo la mayor parte del tiempo.
Pero hay un truco
Uso de pluck() para extraer IDs de una Colección
Uso básico de pluck('id')
pluck() extrae un atributo dado de cada modelo en la colección y devuelve una nueva colección que contiene esos valores.
$permissionIDs = $role->permissions->pluck('id'); Luego puedes llamar a ->toArray() si realmente necesitas un array plano.
Cuándo pluck() es la opción más segura
Por experiencia, pluck() es la opción predeterminada más segura cuando:
- Quieres una columna específica
- Es posible que luego cambies el nombre de la clave
- No estás 100% seguro de cómo se construyó la colección
Funciona en:
- Constructores de consultas (Query builders)
- Modelos de Eloquent
- Colecciones de Eloquent
Esa flexibilidad por sí sola hace que sea difícil de superar.
Formateo de valores con cierres (closures) en pluck()
Aquí es donde pluck() realmente brilla, especialmente después de que se introdujera el soporte para cierres.
Por ejemplo:
Product::available()->get() ->pluck(fn ($product) => "{$product->brand} {$product->model}", 'id'); Sin map() extra. Sin mapWithKeys(). Sin transformaciones intermedias.
He usado mucho esto en vistas de Blade para menús desplegables. Mantiene clara la intención y el código encadenable.
Uso de modelKeys(): qué hace realmente
A primera vista, modelKeys() parece un atajo ingenioso:
$permissionIDs = $role->permissions->modelKeys(); Mismo resultado. Misma cantidad de caracteres. Un poco más "cool"
Pero, ¿qué está haciendo realmente?
Cómo funciona modelKeys() internamente
modelKeys() simplemente devuelve:
“El array de claves primarias de los modelos en la colección.”
Eso es todo.
Sin transformaciones. Sin formateo. Sin flexibilidad.
Claves primarias y nombres de clave no estándar
Una ventaja de modelKeys() es que respeta las claves primarias personalizadas.
Si tu modelo usa algo distinto a id, modelKeys() seguirá funcionando correctamente — mientras que pluck('id') obviamente fallaría a menos que cambies el nombre de la columna.
Esa suele ser la primera razón por la que los desarrolladores notan este método.
Por qué modelKeys() solo funciona en Colecciones
Esta parte confunde a la gente todo el tiempo.
❌ Esto fallará:
Permission::modelKeys(); Porque modelKeys() no existe en el modelo ni en el constructor de consultas.
✅ Esto funciona:
Permission::all()->modelKeys(); Pero aquí está la trampa — y esto es importante en proyectos reales.
Cargar todos los registros en memoria solo para extraer los IDs puede ser un problema de rendimiento serio en tablas grandes. He visto esto hecho accidentalmente más de una vez.
pluck() vs modelKeys(): diferencias clave
Flexibilidad vs simplicidad
pluck()→ flexible, expresivo, funciona en todas partesmodelKeys()→ simple, estricto, solo para colecciones
Si necesitas cualquier cosa más allá de “simplemente dame las claves primarias”, pluck() gana de inmediato.
Consideraciones de rendimiento
En la práctica:
pluck('id')puede ejecutarse a nivel de consulta (base de datos)modelKeys()requiere una colección ya cargada
Eso por sí solo convierte a pluck() en la mejor opción cuando el rendimiento importa.
Errores comunes y conceptos erróneos
Un malentendido común es pensar que modelKeys() es de alguna manera “más eficiente” porque suena a un nivel más bajo.
No lo es.
Si ya tienes la colección cargada, claro — está bien.
Si no la tienes, forzar un all() completo solo para llamar a modelKeys() suele ser una mala idea.
Errores comunes al extraer valores de modelos de Eloquent
Por qué only() no funciona en los atributos del modelo
Esto surge a menudo (y hay un hilo clásico de Laracasts al respecto).
only() funciona sobre las claves de la colección, no sobre los atributos del modelo.
Así que esto devolverá una colección vacía:
$options->only('responses'); Porque responses vive dentro de cada modelo, no al nivel de la colección.
Colección vs Modelo: el modelo mental
Este es el cambio mental clave que resuelve la mayoría de las confusiones:
- Las colecciones contienen modelos
- Los métodos de colección no acceden mágicamente a los atributos del modelo
Una vez que interiorizas eso, pluck() de repente tiene todo el sentido del mundo — y métodos como only() dejan de ser tentadores en los lugares equivocados.
¿Cuál deberías usar en proyectos reales?
Reglas de decisión claras
Usa pluck() cuando:
- No tienes una colección ya cargada
- Necesitas flexibilidad o formateo
- El rendimiento es importante
- Quieres un comportamiento predecible en todas partes
Usa modelKeys() cuando:
- Ya tienes una colección cargada
- Solo te interesan las claves primarias
- El nombre de la clave primaria puede variar
Lo que realmente uso la mayor parte del tiempo
En aplicaciones Laravel del mundo real, sigo usando pluck('id') por defecto.
Es útil conocer modelKeys() — y me alegra que exista — pero es más un método de “interesante de saber” que algo que utilice a diario.
Preguntas frecuentes
¿Es modelKeys() más rápido que pluck()?
No. En todo caso, pluck() suele ser más eficiente porque puede ejecutarse a nivel de consulta.
¿Puede pluck() devolver valores formateados?
Sí. Con cierres (closures), es extremadamente potente y reemplaza muchos casos de uso de map().
¿Funciona modelKeys() en modelos de Eloquent?
No. Solo en colecciones de Eloquent.
¿Puedo obtener IDs sin cargar los modelos completos?
Sí — usa pluck() directamente en la consulta (query).
Conclusión
Tanto pluck() como modelKeys() resuelven el mismo problema a nivel superficial, pero viven en capas diferentes de Eloquent.
Si entiendes cuándo estás tratando con una colección frente a una consulta, la elección correcta se vuelve obvia casi siempre.
Conoce modelKeys().
Usa pluck().
Métodos Model Casts a partir de Laravel 11
El casting o casteo de los modelos en Laravel es una herramienta poderosa para transformar valores de atributos en otros valores, por ejemplo, podemos convertir textos a fechas o a números si es el caso, y de esta forma aprovechar todas las funciones que provee el ecosistema de Laravel como Carbon en el caso de las fechas.
En Laravel, es posible csasteas a tipos primitivos como int, float, boolean, string, entre otros como fechas, la cual al ser de tipo Datetime automáticamente se puede convetir a Carbon, que es una librería empleada por Laravel para facilitar la manipulación de las fechas.
Las conversiones de modelos en Laravel 10 se definen mediante la propiedad de matriz $casts. Sin embargo, en Laravel 11, puedes definir un método casts(), que abre la posibilidad de usar métodos estáticos de maenra personalizada:
use App\Enums\UserOption;
use Illuminate\Database\Eloquent\Casts\AsEnumCollection;
// ...
/**
* Get the attributes that should be cast.
*
* @return array<string, string>
*/
protected function casts(): array
{
return [
'email_verified_at' => 'datetime',
'password' => 'hashed',
'options' => AsEnumCollection::of(UserOption::class),
];
}En Laravel 10, la misma conversión se vería así, ya que no se pueden llamar métodos estáticos al definir una propiedad array:
protected $casts = [
'options' => AsEnumCollection::class.':'.UserOption::class,
];Esta actualización es compatible con versiones anteriores de Laravel 10 y aún puede definir conversiones a través de la propiedad $casts combinada con el nuevo método casts(). La propiedad $casts y el método casts() se fusionan, y las claves del método tienen prioridad sobre la propiedad $casts. Aunque lo recomendado es emplear el casteo con la versión del método para aprovechar los métodos estáticos.