SortIterator.php 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\Collection\Iterator;
  17. use Cake\Chronos\Chronos;
  18. use Cake\Chronos\ChronosDate;
  19. use Cake\Collection\Collection;
  20. use DateTimeInterface;
  21. use Iterator;
  22. use const SORT_DESC;
  23. use const SORT_NUMERIC;
  24. /**
  25. * An iterator that will return the passed items in order. The order is given by
  26. * the value returned in a callback function that maps each of the elements.
  27. *
  28. * ### Example:
  29. *
  30. * ```
  31. * $items = [$user1, $user2, $user3];
  32. * $sorted = new SortIterator($items, function ($user) {
  33. * return $user->age;
  34. * });
  35. *
  36. * // output all user name order by their age in descending order
  37. * foreach ($sorted as $user) {
  38. * echo $user->name;
  39. * }
  40. * ```
  41. *
  42. * This iterator does not preserve the keys passed in the original elements.
  43. */
  44. class SortIterator extends Collection
  45. {
  46. /**
  47. * Wraps this iterator around the passed items so when iterated they are returned
  48. * in order.
  49. *
  50. * The callback will receive as first argument each of the elements in $items,
  51. * the value returned in the callback will be used as the value for sorting such
  52. * element. Please note that the callback function could be called more than once
  53. * per element.
  54. *
  55. * @param iterable $items The values to sort
  56. * @param callable|string $callback A function used to return the actual value to
  57. * be compared. It can also be a string representing the path to use to fetch a
  58. * column or property in each element
  59. * @param int $dir either SORT_DESC or SORT_ASC
  60. * @param int $type the type of comparison to perform, either SORT_STRING
  61. * SORT_NUMERIC or SORT_NATURAL
  62. */
  63. public function __construct(
  64. iterable $items,
  65. callable|string $callback,
  66. int $dir = SORT_DESC,
  67. int $type = SORT_NUMERIC
  68. ) {
  69. if (!is_array($items)) {
  70. $items = iterator_to_array((new Collection($items))->unwrap(), false);
  71. }
  72. $callback = $this->_propertyExtractor($callback);
  73. $results = [];
  74. foreach ($items as $key => $val) {
  75. $val = $callback($val);
  76. $isDateTime = $val instanceof DateTimeInterface || $val instanceof Chronos || $val instanceof ChronosDate;
  77. if ($isDateTime && $type === SORT_NUMERIC) {
  78. $val = $val->format('U');
  79. }
  80. $results[$key] = $val;
  81. }
  82. $dir === SORT_DESC ? arsort($results, $type) : asort($results, $type);
  83. foreach (array_keys($results) as $key) {
  84. $results[$key] = $items[$key];
  85. }
  86. parent::__construct($results);
  87. }
  88. /**
  89. * {@inheritDoc}
  90. *
  91. * @return \Iterator
  92. */
  93. public function unwrap(): Iterator
  94. {
  95. return $this->getInnerIterator();
  96. }
  97. }