Genera URLs usando Eloquent en Laravel de forma sencilla

Para generar las URLs de un modelo Eloquent de forma limpia, puedes usar un "presenter": una clase que expone las rutas del modelo como si fueran propiedades ($user->url->show), en vez de llenar tus vistas de llamadas a route(). La idea la popularizó Jordan Dalton en Laravel News; aquí te muestro cómo la adapté para no tener que crear una clase por cada modelo.
El problema
El patrón original crea un presenter por modelo (UserUrlPresenter, PostUrlPresenter, etc.). Funciona, pero si tienes veinte modelos terminas con veinte clases casi idénticas que solo cambian el nombre de la ruta. Quería una sola clase que sirviera para todos.
La solución: un presenter reutilizable
La clave es pasarle al presenter el nombre del modelo como parámetro, y usarlo para construir el nombre de la ruta:
<?php
namespace App\Presenters;
use Illuminate\Database\Eloquent\Model;
class UrlPresenter
{
public function __construct(
protected Model $model,
protected string $modelName,
) {}
public function __get($key)
{
if (method_exists($this, $key)) {
return $this->$key();
}
return $this->$key;
}
public function show(): string
{
return route("{$this->modelName}.show", $this->model);
}
}__get() hace la magia: cuando accedes a $user->url->show, PHP llama al método show() y este devuelve la ruta con nombre correspondiente. Como el nombre del recurso se pasa por el constructor, la misma clase sirve para cualquier modelo.
Cómo usarlo en el modelo
En cada modelo defines un accessor que devuelve el presenter con su nombre de recurso:
public function getUrlAttribute(): UrlPresenter
{
return new UrlPresenter($this, 'users');
}Y en la vista queda tan limpio como esto:
<a href="{{ $user->url->show }}">{{ $user->name }}</a>Para otro modelo solo cambias el segundo parámetro ('posts', 'products', etc.) y reutilizas la misma clase UrlPresenter. Si necesitas más rutas (edit, destroy), agregas los métodos correspondientes al presenter una sola vez y todos los modelos los heredan.
Cuándo conviene este patrón
- Cuando tienes muchos modelos con rutas de recurso estándar (
resourceroutes) y quieres evitar repetirroute()por todas las vistas. - Cuando prefieres que las vistas sean declarativas (
$user->url->show) en vez de acopladas a los nombres exactos de tus rutas.
Si solo tienes uno o dos modelos, quizá no compense; el route() directo está bien. Como todo patrón, es una herramienta para cuando la repetición empieza a doler.
Preguntas frecuentes
¿Qué es un presenter en Laravel?
Es un objeto que envuelve un modelo para exponer lógica de presentación (como generar URLs o formatear datos) sin ensuciar el modelo ni la vista. Aquí lo usamos para que las rutas del modelo se accedan como propiedades.
¿Por qué usar un presenter en vez de llamar a route() en la vista?
Para no acoplar tus vistas a los nombres exactos de las rutas y evitar repetir la misma llamada por todas partes. Si cambias una ruta, lo ajustas en un solo lugar.
¿Cómo evito crear una clase de URL por cada modelo?
Pasando el nombre del recurso como parámetro al presenter (como en el ejemplo), de modo que una única clase UrlPresenter sirva para todos tus modelos.