Saltar al contenido

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 atributo points aparezca al serializar o convertir a array.

  • getPointsAttribute(): Calculamos la suma de points entre posts y forums.

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í?

  1. withCount en Laravel permite contar los registros relacionados, pero también podemos usar una subconsulta con DB::raw('sum(points)').

  2. Renombramos esas subconsultas a forum_points y post_points.

  3. 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 (withCount o withSum) son más eficientes cuando ordenas o filtras desde la base de datos, evitando N+1 queries.

  • Atributos Modernos con Attribute se 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!

school Curso completo

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.

access_time 8 horas de contenido
layers 4 tecnologías en 1
update 100% actualizado
code Proyectos prácticos
Ver Curso Laravel 12 arrow_forward

star Incluido en cualquier suscripción

Rutas de aprendizaje