
Funcionalidad de Favoritos ("Me gusta") en Laravel con overtrue/laravel-favorite
Agregar una característica de "Favoritos" o "Me gusta" permite a los usuarios marcar contenido que les interesa, mejorando la interacción en tu aplicación (piensa en marcar posts favoritos, productos deseados, etc.). Implementar esto desde cero implica manejar relaciones polimórficas complejas, pero el paquete overtrue/laravel-favorite lo ofrece ya hecho. Este paquete utiliza una relación polimórfica para que cualquier modelo (Post, Producto, Comentario, etc.) pueda ser marcado como favorito por los usuarios. Aquí explicamos cómo integrarlo en Laravel, cómo obtener conteos y verificaciones, y buenas prácticas de rendimiento (incluyendo uso opcional de caché).
Instalación y Configuración del Paquete
Instalación: Ejecuta en tu proyecto:
composer require overtrue/laravel-favorite
Esto añadirá el paquete de favoritos.
Publicar configuración y migraciones: El paquete incluye migraciones para la tabla de favoritos y posiblemente un archivo de config. Publica con:
php artisan vendor:publish --provider="Overtrue\LaravelFavorite\FavoriteServiceProvider"
Esto creará la migración (por ejemplo,
) en tu proyecto. Revisa el archivo de migración para entender la estructura: típicamente contiene camposcreate_favorites_table
(quién marca),user_id
yfavoriteable_id
(el modelo marcado), timestamps, y quizás índices para eficiencia.favoriteable_type
Migrar la base de datos: Corre
. Se creará la tablaphp artisan migrate
(u nombre similar definido por el paquete) para almacenar cada "favorito" como una relación entre un usuario y un modelo (favoriteable).favorites
Implementación de la Relación Polimórfica en Modelos
Overtrue/laravel-favorite funciona mediante traits que habilitan funcionalidad en tus modelos:
En tu modelo User (o aquel que represente al que marca favoritos), usa el trait
:Favoriter
use Overtrue\LaravelFavorite\Traits\Favoriter; class User extends Authenticatable { use Favoriter; // ... }
Al usar
, el usuario obtiene métodos para marcar y desmarcar favoritos, así como relaciones con sus favoritos.Favoriter
En cada modelo que pueda ser marcado como favorito (ej: Post, Video, Producto), usa el trait
:Favoriteable
use Overtrue\LaravelFavorite\Traits\Favoriteable; class Post extends Model { use Favoriteable; // ... }
Esto permite que ese modelo "reciba" favoritos de usuarios, añadiendo la relación polimórfica correspondiente.
Tras esto, User podrá marcar favoritos y Post conocer qué usuarios lo han marcado, sin pasos adicionales.
Marcar y Desmarcar Favoritos (API del Paquete)
El paquete ofrece métodos muy simples para la acción de "favorito":
Marcar como favorito:
$user->favorite($post);
Esto crea un registro de favorito donde el usuario (p.ej. ID 1) marca el post (ID 2). Si el favorito ya existía, no duplica (generalmente la tabla tiene clave única compuesta).
Remover favorito:
$user->unfavorite($post);
Elimina el favorito existente. Úsalo, por ejemplo, cuando el usuario hace clic de nuevo para quitar de favoritos.
Toggle (alternar):
$user->toggleFavorite($post);
Marca o desmarca automáticamente: si el $post no estaba en favoritos del usuario, lo agrega; si ya lo estaba, lo quita. Útil para implementaciones de botón tipo "thumbs-up" que alternan.
Estas operaciones funcionan con cualquier modelo que use
Favoriteable
, gracias a la relación polimórfica. Internamente, el método sabe qué IDs usar y en qué tabla guardar.Consultar Favoritos: Conteos y Verificaciones
Una vez tenemos usuarios marcando elementos, necesitaremos:
Verificar si un usuario marcó X como favorito: El trait provee:
$user->hasFavorited($post);
Retorna
/true
según el usuario haya marcado ese $post. Inversamente, también puedes preguntarle al objeto:false
$post->hasBeenFavoritedBy($user);
que es equivalente (útil quizá por legibilidad en ciertas circunstancias).
Obtener todos los favoritos de un usuario:
$user->favorites(); // relación hasMany polimórfica (Favorite model) $user->getFavoriteItems(Post::class);
El método
devuelve un query builder con todos los modelos de ese tipo que el usuario ha marcado . Por ejemplo,getFavoriteItems(Model::class)
te da una colección de Posts favoritos del usuario. Puedes incluso paginar o agregar condiciones a ese query (p.ej., filtrar favoritos por categoría). Si no especificas clase,$user->getFavoriteItems(Post::class)->get()
te da la relación general, que puedes filtrar confavorites()
.->withType(Model::class)
Listar usuarios que marcaron un objeto: Desde el lado del modelo favorito:
$post->favoriters; // colección de User que lo marcaron $post->favoriters()->count(); // número de usuarios que lo han marcado
es la relación muchos a muchos polimórfica inversa. Un loopfavoriters()
permitiría iterar por los usuarios fans de ese post.foreach($post->favoriters as $user)
Conteos rápidos con withCount: Laravel permite agregar conteos de relaciones fácilmente. Por ejemplo, para obtener una lista de posts con el número de veces que han sido marcados, puedes hacer:
$posts = Post::withCount('favoriters')->get(); foreach($posts as $post) { echo $post->favoriters_count; }
Esto añade el atributo
a cada Post. Similarmente,favoriters_count
daría cuántos favoritos ha hecho cada usuario (atributoUser::withCount('favorites')->get()
).favorites_count
El paquete también incluye un método útil para adornar colecciones con la información de si el usuario actual las ha marcado:
$user->attachFavoriteStatus($posts);
Si
$posts
es, por ejemplo, una colección de 10 Post, tras llamar a attachFavoriteStatus cada Post tendrá un atributo extra has_favorited
true/false según ese $user los tenga en favoritos. Esto es genial para, por ejemplo, al listar artículos en una vista, saber cuáles mostrar con el ícono de corazón lleno o vacío sin consultas adicionales. attachFavoriteStatus
funciona con colecciones, paginadores e incluso arreglos de modelos.Performance y Caching: Buenas Prácticas
Cuando se manejan favoritos, especialmente en gran volumen, considera:
Eager Loading para evitar N+1: Si vas a comprobar en masa si ciertos posts son favoritos de un usuario (o viceversa), carga la relación de favoritos por adelantado. Ejemplo:
// En lugar de loop con hasFavorited que consulta cada vez... $posts = Post::with('favoriters')->get(); foreach($posts as $post) { $post->isFavoritedBy($user); }
Aquí usamos
para cargar todos los favoriters de cada post en 2 queries (uno para posts, otro para favoriters relacionados) en lugar de 1 por post. De igual modo,with('favoriters')
podría precargar todos los modelos marcados por cada usuario, si necesitaras esa info. La idea es utilizar las relaciones provistas en lugar de llamarUser::with('favorites.favoriteable')->get()
en bucle sin carga previa (lo cual haría una consulta por iteración). Overtrue destaca esta técnica en su documentación para evitar problemas de rendimiento.hasFavorited
Índices en BD: Asegúrate de que la tabla de favoritos tenga índices adecuados en
y la combinaciónuser_id
. Generalmente la migración ya los incluye. Esto hará que contar o buscar por esas columnas sea rápido. Por ejemplo, el conteo de favoriters de un post (favoriteable_type, favoriteable_id
) internamente hace unfavoriters()->count()
que con índices será eficiente.WHERE favoriteable_type='Post' AND favoriteable_id=...
Caching de conteos: Si anticipas altísima frecuencia de lectura de, digamos, el número de favoritos de un objeto (ej. mostrando "1000 users liked this" en muchos lugares), podrías implementar caching. Opciones: usar el propio
en consultas (Laravel puede cachear queries si configuras la caché de base de datos) o mantener un campo denormalizadowithCount
en la tabla del modelo y actualizarlo mediante eventos. De hecho, podrías escuchar los eventosfavorites_count
yFavorited
que el paquete emite para, por ejemplo, actualizar un contador en la tabla Post o invalidar una entrada en Redis que lleve el conteo. Esto es opcional y añade complejidad, recomendable solo si el conteo de favoritos se vuelve un cuello de botella comprobado.Unfavorited
Expirar caché al modificar: Si optas por cachear (por ejemplo, guardar en caché la lista de IDs favoritos de un usuario), recuerda siempre invalidar o actualizar la caché cuando un usuario agregue o quite un favorito. El paquete en sí no usa caché interna para favoritos, así que cualquier caching sería implementado por ti a nivel de aplicación.
En la mayoría de aplicaciones, simplemente usando las relaciones provistas y asegurando índices, overtrue/laravel-favorite funcionará eficientemente. Este paquete te ahorra construir la relación polimórfica manualmente y manejar la lógica de favoritos. Con él, añadir "me gusta" o favoritos es tan sencillo como usar unos cuantos métodos, y tienes acceso inmediato a consultas útiles (conteos, listas) sin escribir SQL personalizado.
En resumen, overtrue/laravel-favorite ofrece una solución elegante para incorporar funcionalidad de favoritos en Laravel. Aplica las buenas prácticas mencionadas (carga anticipada, índices, eventos para actualizaciones masivas si aplica) y podrás escalar esta característica manteniendo un rendimiento óptimo incluso con muchos usuarios y muchos favoritos registrados. ¡Tus usuarios apreciarán poder marcar sus contenidos preferidos y tú tendrás una implementación mantenible y limpia!