vendor/knplabs/knp-components/src/Knp/Component/Pager/Event/Subscriber/Sortable/ArraySubscriber.php line 40

  1. <?php
  2. namespace Knp\Component\Pager\Event\Subscriber\Sortable;
  3. use Knp\Component\Pager\ArgumentAccess\ArgumentAccessInterface;
  4. use Knp\Component\Pager\Event\ItemsEvent;
  5. use Knp\Component\Pager\Exception\InvalidValueException;
  6. use Knp\Component\Pager\PaginatorInterface;
  7. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  8. use Symfony\Component\PropertyAccess\Exception\UnexpectedTypeException;
  9. use Symfony\Component\PropertyAccess\PropertyAccess;
  10. use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
  11. class ArraySubscriber implements EventSubscriberInterface
  12. {
  13.     /**
  14.      * @var string the field used to sort current object array list
  15.      */
  16.     private string $currentSortingField;
  17.     /**
  18.      * @var string the sorting direction
  19.      */
  20.     private string $sortDirection;
  21.     private ?PropertyAccessorInterface $propertyAccessor;
  22.     private ArgumentAccessInterface $argumentAccess;
  23.     public function __construct(ArgumentAccessInterface $argumentAccessPropertyAccessorInterface $accessor null)
  24.     {
  25.         if (!$accessor && class_exists(PropertyAccess::class)) {
  26.             $accessor PropertyAccess::createPropertyAccessorBuilder()->enableMagicCall()->getPropertyAccessor();
  27.         }
  28.         $this->propertyAccessor $accessor;
  29.         $this->argumentAccess $argumentAccess;
  30.     }
  31.     public function items(ItemsEvent $event): void
  32.     {
  33.         // Check if the result has already been sorted by another sort subscriber
  34.         $customPaginationParameters $event->getCustomPaginationParameters();
  35.         if (!empty($customPaginationParameters['sorted']) ) {
  36.             return;
  37.         }
  38.         $sortField $event->options[PaginatorInterface::SORT_FIELD_PARAMETER_NAME];
  39.         if (!is_array($event->target) || null === $sortField || !$this->argumentAccess->has($sortField)) {
  40.             return;
  41.         }
  42.         $event->setCustomPaginationParameter('sorted'true);
  43.         if (isset($event->options[PaginatorInterface::SORT_FIELD_ALLOW_LIST]) && !in_array($this->argumentAccess->get($sortField), $event->options[PaginatorInterface::SORT_FIELD_ALLOW_LIST])) {
  44.             throw new InvalidValueException("Cannot sort by: [{$this->argumentAccess->get($sortField)}] this field is not in allow list.");
  45.         }
  46.         $sortFunction $event->options['sortFunction'] ?? [$this'proxySortFunction'];
  47.         $sortField $this->argumentAccess->get($sortField);
  48.         // compatibility layer
  49.         if ($sortField[0] === '.') {
  50.             $sortField substr($sortField1);
  51.         }
  52.         call_user_func_array($sortFunction, [
  53.             &$event->target,
  54.             $sortField,
  55.             $this->getSortDirection($event->options),
  56.         ]);
  57.     }
  58.     private function getSortDirection(array $options): string
  59.     {
  60.         if (!$this->argumentAccess->has($options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME])) {
  61.             return 'desc';
  62.         }
  63.         $direction $this->argumentAccess->get($options[PaginatorInterface::SORT_DIRECTION_PARAMETER_NAME]);
  64.         if (strtolower($direction) === 'asc') {
  65.             return 'asc';
  66.         }
  67.         return 'desc';
  68.     }
  69.     private function proxySortFunction(&$target$sortField$sortDirection): bool
  70.     {
  71.         $this->currentSortingField $sortField;
  72.         $this->sortDirection $sortDirection;
  73.         return usort($target, [$this'sortFunction']);
  74.     }
  75.     private function sortFunction(object|array $object1object|array $object2): int
  76.     {
  77.         if (null === $this->propertyAccessor) {
  78.             throw new \UnexpectedValueException('You need symfony/property-access component to use this sorting function');
  79.         }
  80.         if (!$this->propertyAccessor->isReadable($object1$this->currentSortingField) || !$this->propertyAccessor->isReadable($object2$this->currentSortingField)) {
  81.             return 0;
  82.         }
  83.         try {
  84.             $fieldValue1 $this->propertyAccessor->getValue($object1$this->currentSortingField);
  85.         } catch (UnexpectedTypeException) {
  86.             return -$this->getSortCoefficient();
  87.         }
  88.         try {
  89.             $fieldValue2 $this->propertyAccessor->getValue($object2$this->currentSortingField);
  90.         } catch (UnexpectedTypeException) {
  91.             return $this->getSortCoefficient();
  92.         }
  93.         if (is_string($fieldValue1)) {
  94.             $fieldValue1 mb_strtolower($fieldValue1);
  95.         }
  96.         if (is_string($fieldValue2)) {
  97.             $fieldValue2 mb_strtolower($fieldValue2);
  98.         }
  99.         if ($fieldValue1 === $fieldValue2) {
  100.             return 0;
  101.         }
  102.         return ($fieldValue1 $fieldValue2 : -1) * $this->getSortCoefficient();
  103.     }
  104.     private function getSortCoefficient(): int
  105.     {
  106.         return $this->sortDirection === 'asc' : -1;
  107.     }
  108.     public static function getSubscribedEvents(): array
  109.     {
  110.         return [
  111.             'knp_pager.items' => ['items'1],
  112.         ];
  113.     }
  114. }