En ocasiones, necesitas ordenar tus modelos en base a la suma de ciertos valores en relaciones distintas. Por ejemplo, un usuario puede tener puntos acumulados tanto en sus foros como en sus posts. A continuación, veremos cómo lograrlo usando Eloquent de una forma limpia y optimizada.
1. Definir la Relación y el Atributo Calculado
1.1. El Modelo User
En el siguiente ejemplo, cada usuario (User) tiene muchas publicaciones (Post) y muchos foros (Forum). Queremos calcular la cantidad total de puntos que ha obtenido sumando los puntos de todas sus publicaciones y foros.
Usando el Mecanismo Clásico (getPointsAttribute)
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Support\Str;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
protected $appends = ['points'];
public function forums(): HasMany
{
return $this->hasMany(Forum::class);
}
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
/**
* Suma los puntos de foros y posts.
*/
public function getPointsAttribute(): int
{
return $this->posts()->sum('points') + $this->forums()->sum('points');
}
}Puntos clave:
protected $appends = ['points']: Forzamos a que el atributopointsaparezca al serializar o convertir a array.getPointsAttribute(): Calculamos la suma depointsentrepostsyforums.
1.2. Usando Atributos Modernos con Casts\Attribute
A partir de Laravel 8.42 se recomienda usar la clase Attribute. Quedaría así:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
public function forums(): HasMany
{
return $this->hasMany(Forum::class);
}
public function posts(): HasMany
{
return $this->hasMany(Post::class);
}
/**
* Atributo points usando Eloquent Attributes.
*/
protected function points(): Attribute
{
return Attribute::make(
get: fn () => $this->posts()->sum('points') + $this->forums()->sum('points'),
);
}
}Ventajas:
Sintaxis más clara y organizada.
Mantiene la filosofía de getters/setters “nuevos” en Laravel.
2. Consultar y Ordenar Usando Sumatorios
Para ordenar a los usuarios según sus puntos de posts y forums, usaremos la función de Eloquent. Hay dos enfoques principales:
2.1. Usar withCount con DB::raw
<?php
use Illuminate\Support\Facades\DB;
use App\Models\User;
$users = User::select(['id'])
->withCount([
'forums as forum_points' => fn ($query) => $query->select(DB::raw('sum(points)')),
'posts as post_points' => fn ($query) => $query->select(DB::raw('sum(points)')),
])
->orderByRaw('forum_points + post_points DESC')
->get();¿Qué sucede aquí?
withCount en Laravel permite contar los registros relacionados, pero también podemos usar una subconsulta con
DB::raw('sum(points)').Renombramos esas subconsultas a
forum_pointsypost_points.Luego ordenamos usando
orderByRaw('forum_points + post_points DESC').
2.2. Usar withSum (si tu versión de Laravel lo permite)
A partir de Laravel 8.x se incluyó withSum, que hace más explícito lo que queremos:
<?php
$users = User::withSum('forums as forum_points', 'points')
->withSum('posts as post_points', 'points')
->orderByRaw('forum_points + post_points DESC')
->get();De esta forma, Laravel generará subconsultas que calculen la suma de points para cada relación.
Nota: Al terminar, cada User tendrá las propiedades forum_points y post_points disponibles.
3. Comparación y Recomendaciones
Atributo en el Modelo (
points) es ideal para mostrar la información en tus vistas (o en tu API).Subconsultas (
withCountowithSum) son más eficientes cuando ordenas o filtras desde la base de datos, evitando N+1 queries.Atributos Modernos con
Attributese ajustan a las recomendaciones recientes de Laravel, haciendo el código más limpio y mantenible.
4. Conclusión
Combinar subconsultas (withSum o withCount + DB::raw) con un atributo calculado en el modelo te permite mostrar y ordenar los datos de la forma más óptima. Así, no solo ofreces datos actualizados y centralizados en el modelo, sino que también evitas múltiples consultas innecesarias.
¿Ya has implementado sumatorios en tus relaciones para ordenar tus resultados? ¡Comparte tu experiencia con tus colegas y no olvides explorar la Ruta de Aprendizaje Laravel 11!
Curso Laravel 12
Completo 2026
El único curso 100% actualizado que incluye Laravel 12, Livewire 3, Vue 3, React 19 e Inertia 2. Aprende con proyectos reales y las últimas funcionalidades.
star Incluido en cualquier suscripción