AzuraCast/src/Controller/Api/Traits/CanSortResults.php

116 lines
3.4 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Controller\Api\Traits;
use App\Http\ServerRequest;
use App\Utilities\Types;
use Doctrine\Common\Collections\Criteria;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\PropertyAccess\PropertyAccess;
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
trait CanSortResults
{
protected function sortQueryBuilder(
ServerRequest $request,
QueryBuilder $queryBuilder,
array $sortLookup,
?string $defaultSort,
string $defaultSortOrder = Criteria::ASC
): QueryBuilder {
[$sort, $sortOrder] = $this->getSortFromRequest($request, $defaultSortOrder);
$sortValue = (null !== $sort && isset($sortLookup[$sort]))
? $sortLookup[$sort]
: $defaultSort;
if (null === $sortValue) {
return $queryBuilder;
}
return $queryBuilder->addOrderBy($sortValue, $sortOrder);
}
protected function sortArray(
ServerRequest $request,
array $results,
array $sortLookup,
?string $defaultSort = null,
string $defaultSortOrder = Criteria::ASC
): array {
[$sort, $sortOrder] = $this->getSortFromRequest($request, $defaultSortOrder);
$sortValue = (null !== $sort && isset($sortLookup[$sort]))
? $sortLookup[$sort]
: $defaultSort;
if (null === $sortValue) {
return $results;
}
$propertyAccessor = self::getPropertyAccessor();
usort(
$results,
static fn(mixed $a, mixed $b) => self::sortByDotNotation($a, $b, $propertyAccessor, $sortValue, $sortOrder)
);
return $results;
}
/**
* @return array{string|null, Criteria::ASC|Criteria::DESC}
*/
protected function getSortFromRequest(
ServerRequest $request,
string $defaultSortOrder = Criteria::ASC
): array {
$sortOrder = Types::stringOrNull($request->getParam('sortOrder'), true) ?? $defaultSortOrder;
return [
Types::stringOrNull($request->getParam('sort'), true),
(Criteria::DESC === strtoupper($sortOrder))
? Criteria::DESC
: Criteria::ASC,
];
}
protected static function sortByDotNotation(
object|array $a,
object|array $b,
PropertyAccessorInterface $propertyAccessor,
string $sortValue,
string $sortOrder
): int {
$aVal = $propertyAccessor->getValue($a, $sortValue);
$bVal = $propertyAccessor->getValue($b, $sortValue);
if (is_string($aVal)) {
$aVal = mb_strtolower($aVal, 'UTF-8');
}
if (is_string($bVal)) {
$bVal = mb_strtolower($bVal, 'UTF-8');
}
return (Criteria::ASC === $sortOrder)
? $aVal <=> $bVal
: $bVal <=> $aVal;
}
protected static ?PropertyAccessorInterface $propertyAccessor = null;
protected static function getPropertyAccessor(): PropertyAccessorInterface
{
if (null === self::$propertyAccessor) {
self::$propertyAccessor = PropertyAccess::createPropertyAccessorBuilder()
->disableExceptionOnInvalidIndex()
->disableExceptionOnInvalidPropertyPath()
->disableMagicMethods()
->getPropertyAccessor();
}
return self::$propertyAccessor;
}
}