<?phpnamespace Knp\Component\Pager\Event\Subscriber\Sortable;use Knp\Component\Pager\Event\ItemsEvent;use Knp\Component\Pager\PaginatorInterface;use Symfony\Component\EventDispatcher\EventSubscriberInterface;use Symfony\Component\HttpFoundation\Request;use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;use Symfony\Component\PropertyAccess\PropertyAccess;use Symfony\Component\PropertyAccess\PropertyAccessorInterface;class ArraySubscriber implements EventSubscriberInterface{ /** * @var string the field used to sort current object array list */ private string $currentSortingField; /** * @var string the sorting direction */ private string $sortDirection; private ?PropertyAccessorInterface $propertyAccessor; private Request $request; public function __construct(Request $request = null, PropertyAccessorInterface $accessor = null) { if (!$accessor && class_exists(PropertyAccess::class)) { $accessor = PropertyAccess::createPropertyAccessorBuilder()->enableMagicCall()->getPropertyAccessor(); } $this->propertyAccessor = $accessor; // check needed because $request must be nullable, being the second parameter (with the first one nullable) if (null === $request) { throw new \InvalidArgumentException('Request must be initialized.'); } $this->request = $request; } public function items(ItemsEvent $event): void { // Check if the result has already been sorted by an other sort subscriber $customPaginationParameters = $event->getCustomPaginationParameters(); if (!empty($customPaginationParameters['sorted']) ) { return; } $sortField = $event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME]; if (!is_array($event->target) || null === $sortField || !$this->request->query->has($sortField)) { return; } $event->setCustomPaginationParameter('sorted', true); if (isset($event->options[PaginatorInterface::SORT_FIELD_ALLOW_LIST]) && !in_array($this->request->query->get($sortField), $event->options[PaginatorInterface::SORT_FIELD_ALLOW_LIST])) { throw new \UnexpectedValueException("Cannot sort by: [{$this->request->query->get($sortField)}] this field is not in allow list."); } $sortFunction = $event->options['sortFunction'] ?? [$this, 'proxySortFunction']; $sortField = $this->request->query->get($sortField); // compatibility layer if ($sortField[0] === '.') { $sortField = substr($sortField, 1); } call_user_func_array($sortFunction, [ &$event->target, $sortField, $this->getSortDirection($event->options), ]); } private function getSortDirection(array $options): string { if (!$this->request->query->has($options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME])) { return 'desc'; } $direction = $this->request->query->get($options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]); if (strtolower($direction) === 'asc') { return 'asc'; } return 'desc'; } private function proxySortFunction(&$target, $sortField, $sortDirection): bool { $this->currentSortingField = $sortField; $this->sortDirection = $sortDirection; return usort($target, [$this, 'sortFunction']); } /** * @param mixed $object1 first object to compare * @param mixed $object2 second object to compare * * @return int */ private function sortFunction($object1, $object2): int { if (null === $this->propertyAccessor) { throw new \UnexpectedValueException('You need symfony/property-access component to use this sorting function'); } if (!$this->propertyAccessor->isReadable($object1, $this->currentSortingField) || !$this->propertyAccessor->isReadable($object2, $this->currentSortingField)) { return 0; } try { $fieldValue1 = $this->propertyAccessor->getValue($object1, $this->currentSortingField); } catch (UnexpectedTypeException $e) { return -1 * $this->getSortCoefficient(); } try { $fieldValue2 = $this->propertyAccessor->getValue($object2, $this->currentSortingField); } catch (UnexpectedTypeException $e) { return 1 * $this->getSortCoefficient(); } if (is_string($fieldValue1)) { $fieldValue1 = mb_strtolower($fieldValue1); } if (is_string($fieldValue2)) { $fieldValue2 = mb_strtolower($fieldValue2); } if ($fieldValue1 === $fieldValue2) { return 0; } return ($fieldValue1 > $fieldValue2 ? 1 : -1) * $this->getSortCoefficient(); } private function getSortCoefficient(): int { return $this->sortDirection === 'asc' ? 1 : -1; } public static function getSubscribedEvents(): array { return [ 'knp_pager.items' => ['items', 1], ]; }}