Sort collection through multiple columns

0

I need to sort a collection first by the field points and then by company_fantasy I tried some forms according to this post link that would be the same problem but did not succeed. I'm using Laravel 5.3

In this way I am sorting by points, but if two companies have the same score, I need them to sort by company_fantasy :

if ($order === self::ORDER_POINTS) {
    $collection = $collection->sortByDesc('points');
}

namespace App\Libella\Library;

use Cache;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Rentalhost\Libella\Company;
use Rentalhost\Libella\Score;
use Rentalhost\Libella\ScoreAssignment;
use Rentalhost\Libella\ScoreMedal;

class CompanyRanking
{
const ORDER_MEDALS = 'medals';
const ORDER_POINTS = 'points';

/**
 * Relação de atribuições à empresa.
 * @var ScoreAssignment[]|Collection
 */
public $assignments;

/**
 * Número de medalhas de bronze.
 * @var int
 */
public $bronzes = 0;

/**
 * Empresa.
 * @var Company
 */
public $company;

/**
 * Número de medalhas de ouro.
 * @var int
 */
public $golds = 0;

/**
 * Relação de medalhas da empresa.
 * @var ScoreMedal[]|Collection
 */
public $medals;

/**
 * Pontuação.
 * @var int
 */
public $points = 0;

/**
 * Número de medalhas de prata.
 * @var int
 */
public $silvers = 0;

/**
 * CompanyRanking constructor.
 * @param Company $company Empresa de referência.
 */
public function __construct(Company $company)
{
    $this->company     = $company;
    $this->medals      = collect();
    $this->assignments = collect();
}

/**
 * Retorna um rankeamento até uma data determinada (ou todas).
 * @param Carbon|null $until Data limite para obter as informações (inclusiva).
 * @param string|null $order Modo de organização.
 * @return CompanyRanking[]|Collection
 */
static public function get($until = null, $order = null)
{
    $order  = $order ?? self::ORDER_POINTS;
    $prefix = static::class;

    if ($until instanceof Carbon) {
        $prefix .= $until->toAtomString() . '.';
    }

    $scores = Score::pluck('id');

    /** @var ScoreMedal[]|Collection $scoreMedals */
    $scoreMedals = Cache::store('array')->rememberForever($prefix . '.scoreMedals', function () use ($scores, $until) {
        /** @var ScoreMedal|Builder $scoresMedalsQuery */
        $scoresMedalsQuery = ScoreMedal::query();
        $scoresMedalsQuery->with('team.company');
        $scoresMedalsQuery->whereIn('score_id', $scores);

        // Aplica a data limite.
        if ($until !== null) {
            $scoresMedalsQuery->whereDate('created_at', '<=', $until);
        }

        return $scoresMedalsQuery->get();
    });

    /** @var ScoreAssignment[]|Collection $assignments */
    $assignments = Cache::store('array')->rememberForever($prefix . '.assignments', function () use ($until) {
        /** @var ScoreAssignment|Builder $assignmentsQuery */
        $assignmentsQuery = ScoreAssignment::query();
        $assignmentsQuery->with('company');

        // Aplica a data limite.
        if ($until !== null) {
            $assignmentsQuery->whereDate('created_at', '<=', $until);
        }

        return $assignmentsQuery->get();
    });

    // Empresas.
    /** @var Company[]|Collection $companies */
    $companiesQuery = Company::query();
    $companiesQuery->orderBy('company_fantasy');
    $companies  = $companiesQuery->get();
    $collection = collect();

    foreach ($companies as $company) {
        $companyRanking              = new static($company);
        $companyRanking->medals      = $scoreMedals->where('team.company_id', $company->id);
        $companyRanking->assignments = $assignments->where('company_id', $company->id);

        // Calcula as medalhas.
        $companyRanking->golds   += $companyRanking->medals->where('medal_ranking', '1')->count();
        $companyRanking->silvers += $companyRanking->medals->where('medal_ranking', '2')->count();
        $companyRanking->bronzes += $companyRanking->medals->where('medal_ranking', '3')->count();

        // Calcula a pontuação.
        $companyRanking->points +=
            $companyRanking->medals->reduce(function ($carry, ScoreMedal $scoreMedal) {
                return $carry + $scoreMedal->getPoints();
            }, 0) +
            $companyRanking->assignments->reduce(function ($carry, ScoreAssignment $scoreAssignment) {
                return $carry + $scoreAssignment->assignment_points;
            }, 0);

        $scoreMedals = $scoreMedals->reject($companyRanking->medals);
        $assignments = $assignments->reject($companyRanking->assignments);

        $collection->put($company->id, $companyRanking);
    }

    if ($order === self::ORDER_POINTS) {
        $collection = $collection->sortByDesc('points');
    }
    else if ($order === self::ORDER_MEDALS) {
        $collection = $collection->sortByDesc(function (CompanyRanking $companyRanking) {
            return $companyRanking->golds * 1e6 +
                   $companyRanking->silvers * 1e3 +
                   $companyRanking->bronzes;
        })->sortBy('company.company_fantasy');
    }

    return $collection;
}

/**
 * Retorna um número de referência para a quantidade de medalhas virtuais.
 * @param int $number Número de medalhas reais.
 * @return int
 */
static public function medalsVirtuals($number)
{
    return min(floor(ceil(sqrt(8 * max(0, $number) + 1)) / 1.8), 7);
}

/**
 * Soma o número de medalhas.
 * @return int
 */
public function sumMedals()
{
    return $this->golds +
           $this->silvers +
           $this->bronzes;
}
}
    
asked by anonymous 06.04.2018 / 00:05

1 answer

0

I was able to solve the problem and I am leaving the solution in case anyone has the same doubt:

if ($order === self::ORDER_POINTS) {
    $collection = $collection->sort(
        function ($a, $b) {
            return $b->points <=> $a->points //ordena por pontos
                ?: $a->company->company_fantasy <=> $b->company->company_fantasy; //caso os pontos sejam iguais, ordena alfabeticamente
            }
    );
}
    
06.04.2018 / 17:57