EnumeratesValues.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152
  1. <?php
  2. namespace Illuminate\Support\Traits;
  3. use CachingIterator;
  4. use Closure;
  5. use Exception;
  6. use Illuminate\Contracts\Support\Arrayable;
  7. use Illuminate\Contracts\Support\Jsonable;
  8. use Illuminate\Support\Arr;
  9. use Illuminate\Support\Collection;
  10. use Illuminate\Support\Enumerable;
  11. use Illuminate\Support\HigherOrderCollectionProxy;
  12. use InvalidArgumentException;
  13. use JsonSerializable;
  14. use Symfony\Component\VarDumper\VarDumper;
  15. use Traversable;
  16. use UnexpectedValueException;
  17. use UnitEnum;
  18. use WeakMap;
  19. /**
  20. * @template TKey of array-key
  21. *
  22. * @template-covariant TValue
  23. *
  24. * @property-read HigherOrderCollectionProxy $average
  25. * @property-read HigherOrderCollectionProxy $avg
  26. * @property-read HigherOrderCollectionProxy $contains
  27. * @property-read HigherOrderCollectionProxy $doesntContain
  28. * @property-read HigherOrderCollectionProxy $each
  29. * @property-read HigherOrderCollectionProxy $every
  30. * @property-read HigherOrderCollectionProxy $filter
  31. * @property-read HigherOrderCollectionProxy $first
  32. * @property-read HigherOrderCollectionProxy $flatMap
  33. * @property-read HigherOrderCollectionProxy $groupBy
  34. * @property-read HigherOrderCollectionProxy $keyBy
  35. * @property-read HigherOrderCollectionProxy $map
  36. * @property-read HigherOrderCollectionProxy $max
  37. * @property-read HigherOrderCollectionProxy $min
  38. * @property-read HigherOrderCollectionProxy $partition
  39. * @property-read HigherOrderCollectionProxy $percentage
  40. * @property-read HigherOrderCollectionProxy $reject
  41. * @property-read HigherOrderCollectionProxy $skipUntil
  42. * @property-read HigherOrderCollectionProxy $skipWhile
  43. * @property-read HigherOrderCollectionProxy $some
  44. * @property-read HigherOrderCollectionProxy $sortBy
  45. * @property-read HigherOrderCollectionProxy $sortByDesc
  46. * @property-read HigherOrderCollectionProxy $sum
  47. * @property-read HigherOrderCollectionProxy $takeUntil
  48. * @property-read HigherOrderCollectionProxy $takeWhile
  49. * @property-read HigherOrderCollectionProxy $unique
  50. * @property-read HigherOrderCollectionProxy $unless
  51. * @property-read HigherOrderCollectionProxy $until
  52. * @property-read HigherOrderCollectionProxy $when
  53. */
  54. trait EnumeratesValues
  55. {
  56. use Conditionable;
  57. /**
  58. * Indicates that the object's string representation should be escaped when __toString is invoked.
  59. *
  60. * @var bool
  61. */
  62. protected $escapeWhenCastingToString = false;
  63. /**
  64. * The methods that can be proxied.
  65. *
  66. * @var array<int, string>
  67. */
  68. protected static $proxies = [
  69. 'average',
  70. 'avg',
  71. 'contains',
  72. 'doesntContain',
  73. 'each',
  74. 'every',
  75. 'filter',
  76. 'first',
  77. 'flatMap',
  78. 'groupBy',
  79. 'keyBy',
  80. 'map',
  81. 'max',
  82. 'min',
  83. 'partition',
  84. 'percentage',
  85. 'reject',
  86. 'skipUntil',
  87. 'skipWhile',
  88. 'some',
  89. 'sortBy',
  90. 'sortByDesc',
  91. 'sum',
  92. 'takeUntil',
  93. 'takeWhile',
  94. 'unique',
  95. 'unless',
  96. 'until',
  97. 'when',
  98. ];
  99. /**
  100. * Create a new collection instance if the value isn't one already.
  101. *
  102. * @template TMakeKey of array-key
  103. * @template TMakeValue
  104. *
  105. * @param \Illuminate\Contracts\Support\Arrayable<TMakeKey, TMakeValue>|iterable<TMakeKey, TMakeValue>|null $items
  106. * @return static<TMakeKey, TMakeValue>
  107. */
  108. public static function make($items = [])
  109. {
  110. return new static($items);
  111. }
  112. /**
  113. * Wrap the given value in a collection if applicable.
  114. *
  115. * @template TWrapValue
  116. *
  117. * @param iterable<array-key, TWrapValue>|TWrapValue $value
  118. * @return static<array-key, TWrapValue>
  119. */
  120. public static function wrap($value)
  121. {
  122. return $value instanceof Enumerable
  123. ? new static($value)
  124. : new static(Arr::wrap($value));
  125. }
  126. /**
  127. * Get the underlying items from the given collection if applicable.
  128. *
  129. * @template TUnwrapKey of array-key
  130. * @template TUnwrapValue
  131. *
  132. * @param array<TUnwrapKey, TUnwrapValue>|static<TUnwrapKey, TUnwrapValue> $value
  133. * @return array<TUnwrapKey, TUnwrapValue>
  134. */
  135. public static function unwrap($value)
  136. {
  137. return $value instanceof Enumerable ? $value->all() : $value;
  138. }
  139. /**
  140. * Create a new instance with no items.
  141. *
  142. * @return static
  143. */
  144. public static function empty()
  145. {
  146. return new static([]);
  147. }
  148. /**
  149. * Create a new collection by invoking the callback a given amount of times.
  150. *
  151. * @template TTimesValue
  152. *
  153. * @param int $number
  154. * @param (callable(int): TTimesValue)|null $callback
  155. * @return static<int, TTimesValue>
  156. */
  157. public static function times($number, ?callable $callback = null)
  158. {
  159. if ($number < 1) {
  160. return new static;
  161. }
  162. return static::range(1, $number)
  163. ->unless($callback == null)
  164. ->map($callback);
  165. }
  166. /**
  167. * Alias for the "avg" method.
  168. *
  169. * @param (callable(TValue): float|int)|string|null $callback
  170. * @return float|int|null
  171. */
  172. public function average($callback = null)
  173. {
  174. return $this->avg($callback);
  175. }
  176. /**
  177. * Alias for the "contains" method.
  178. *
  179. * @param (callable(TValue, TKey): bool)|TValue|string $key
  180. * @param mixed $operator
  181. * @param mixed $value
  182. * @return bool
  183. */
  184. public function some($key, $operator = null, $value = null)
  185. {
  186. return $this->contains(...func_get_args());
  187. }
  188. /**
  189. * Dump the items and end the script.
  190. *
  191. * @param mixed ...$args
  192. * @return never
  193. */
  194. public function dd(...$args)
  195. {
  196. $this->dump(...$args);
  197. exit(1);
  198. }
  199. /**
  200. * Dump the items.
  201. *
  202. * @return $this
  203. */
  204. public function dump()
  205. {
  206. (new Collection(func_get_args()))
  207. ->push($this->all())
  208. ->each(function ($item) {
  209. VarDumper::dump($item);
  210. });
  211. return $this;
  212. }
  213. /**
  214. * Execute a callback over each item.
  215. *
  216. * @param callable(TValue, TKey): mixed $callback
  217. * @return $this
  218. */
  219. public function each(callable $callback)
  220. {
  221. foreach ($this as $key => $item) {
  222. if ($callback($item, $key) === false) {
  223. break;
  224. }
  225. }
  226. return $this;
  227. }
  228. /**
  229. * Execute a callback over each nested chunk of items.
  230. *
  231. * @param callable(...mixed): mixed $callback
  232. * @return static
  233. */
  234. public function eachSpread(callable $callback)
  235. {
  236. return $this->each(function ($chunk, $key) use ($callback) {
  237. $chunk[] = $key;
  238. return $callback(...$chunk);
  239. });
  240. }
  241. /**
  242. * Determine if all items pass the given truth test.
  243. *
  244. * @param (callable(TValue, TKey): bool)|TValue|string $key
  245. * @param mixed $operator
  246. * @param mixed $value
  247. * @return bool
  248. */
  249. public function every($key, $operator = null, $value = null)
  250. {
  251. if (func_num_args() === 1) {
  252. $callback = $this->valueRetriever($key);
  253. foreach ($this as $k => $v) {
  254. if (! $callback($v, $k)) {
  255. return false;
  256. }
  257. }
  258. return true;
  259. }
  260. return $this->every($this->operatorForWhere(...func_get_args()));
  261. }
  262. /**
  263. * Get the first item by the given key value pair.
  264. *
  265. * @param callable|string $key
  266. * @param mixed $operator
  267. * @param mixed $value
  268. * @return TValue|null
  269. */
  270. public function firstWhere($key, $operator = null, $value = null)
  271. {
  272. return $this->first($this->operatorForWhere(...func_get_args()));
  273. }
  274. /**
  275. * Get a single key's value from the first matching item in the collection.
  276. *
  277. * @template TValueDefault
  278. *
  279. * @param string $key
  280. * @param TValueDefault|(\Closure(): TValueDefault) $default
  281. * @return TValue|TValueDefault
  282. */
  283. public function value($key, $default = null)
  284. {
  285. if ($value = $this->firstWhere($key)) {
  286. return data_get($value, $key, $default);
  287. }
  288. return value($default);
  289. }
  290. /**
  291. * Ensure that every item in the collection is of the expected type.
  292. *
  293. * @template TEnsureOfType
  294. *
  295. * @param class-string<TEnsureOfType>|array<array-key, class-string<TEnsureOfType>> $type
  296. * @return static<TKey, TEnsureOfType>
  297. *
  298. * @throws \UnexpectedValueException
  299. */
  300. public function ensure($type)
  301. {
  302. $allowedTypes = is_array($type) ? $type : [$type];
  303. return $this->each(function ($item) use ($allowedTypes) {
  304. $itemType = get_debug_type($item);
  305. foreach ($allowedTypes as $allowedType) {
  306. if ($itemType === $allowedType || $item instanceof $allowedType) {
  307. return true;
  308. }
  309. }
  310. throw new UnexpectedValueException(
  311. sprintf("Collection should only include [%s] items, but '%s' found.", implode(', ', $allowedTypes), $itemType)
  312. );
  313. });
  314. }
  315. /**
  316. * Determine if the collection is not empty.
  317. *
  318. * @return bool
  319. */
  320. public function isNotEmpty()
  321. {
  322. return ! $this->isEmpty();
  323. }
  324. /**
  325. * Run a map over each nested chunk of items.
  326. *
  327. * @template TMapSpreadValue
  328. *
  329. * @param callable(mixed...): TMapSpreadValue $callback
  330. * @return static<TKey, TMapSpreadValue>
  331. */
  332. public function mapSpread(callable $callback)
  333. {
  334. return $this->map(function ($chunk, $key) use ($callback) {
  335. $chunk[] = $key;
  336. return $callback(...$chunk);
  337. });
  338. }
  339. /**
  340. * Run a grouping map over the items.
  341. *
  342. * The callback should return an associative array with a single key/value pair.
  343. *
  344. * @template TMapToGroupsKey of array-key
  345. * @template TMapToGroupsValue
  346. *
  347. * @param callable(TValue, TKey): array<TMapToGroupsKey, TMapToGroupsValue> $callback
  348. * @return static<TMapToGroupsKey, static<int, TMapToGroupsValue>>
  349. */
  350. public function mapToGroups(callable $callback)
  351. {
  352. $groups = $this->mapToDictionary($callback);
  353. return $groups->map([$this, 'make']);
  354. }
  355. /**
  356. * Map a collection and flatten the result by a single level.
  357. *
  358. * @template TFlatMapKey of array-key
  359. * @template TFlatMapValue
  360. *
  361. * @param callable(TValue, TKey): (\Illuminate\Support\Collection<TFlatMapKey, TFlatMapValue>|array<TFlatMapKey, TFlatMapValue>) $callback
  362. * @return static<TFlatMapKey, TFlatMapValue>
  363. */
  364. public function flatMap(callable $callback)
  365. {
  366. return $this->map($callback)->collapse();
  367. }
  368. /**
  369. * Map the values into a new class.
  370. *
  371. * @template TMapIntoValue
  372. *
  373. * @param class-string<TMapIntoValue> $class
  374. * @return static<TKey, TMapIntoValue>
  375. */
  376. public function mapInto($class)
  377. {
  378. return $this->map(fn ($value, $key) => new $class($value, $key));
  379. }
  380. /**
  381. * Get the min value of a given key.
  382. *
  383. * @param (callable(TValue):mixed)|string|null $callback
  384. * @return mixed
  385. */
  386. public function min($callback = null)
  387. {
  388. $callback = $this->valueRetriever($callback);
  389. return $this->map(fn ($value) => $callback($value))
  390. ->filter(fn ($value) => ! is_null($value))
  391. ->reduce(fn ($result, $value) => is_null($result) || $value < $result ? $value : $result);
  392. }
  393. /**
  394. * Get the max value of a given key.
  395. *
  396. * @param (callable(TValue):mixed)|string|null $callback
  397. * @return mixed
  398. */
  399. public function max($callback = null)
  400. {
  401. $callback = $this->valueRetriever($callback);
  402. return $this->filter(fn ($value) => ! is_null($value))->reduce(function ($result, $item) use ($callback) {
  403. $value = $callback($item);
  404. return is_null($result) || $value > $result ? $value : $result;
  405. });
  406. }
  407. /**
  408. * "Paginate" the collection by slicing it into a smaller collection.
  409. *
  410. * @param int $page
  411. * @param int $perPage
  412. * @return static
  413. */
  414. public function forPage($page, $perPage)
  415. {
  416. $offset = max(0, ($page - 1) * $perPage);
  417. return $this->slice($offset, $perPage);
  418. }
  419. /**
  420. * Partition the collection into two arrays using the given callback or key.
  421. *
  422. * @param (callable(TValue, TKey): bool)|TValue|string $key
  423. * @param TValue|string|null $operator
  424. * @param TValue|null $value
  425. * @return static<int<0, 1>, static<TKey, TValue>>
  426. */
  427. public function partition($key, $operator = null, $value = null)
  428. {
  429. $passed = [];
  430. $failed = [];
  431. $callback = func_num_args() === 1
  432. ? $this->valueRetriever($key)
  433. : $this->operatorForWhere(...func_get_args());
  434. foreach ($this as $key => $item) {
  435. if ($callback($item, $key)) {
  436. $passed[$key] = $item;
  437. } else {
  438. $failed[$key] = $item;
  439. }
  440. }
  441. return new static([new static($passed), new static($failed)]);
  442. }
  443. /**
  444. * Calculate the percentage of items that pass a given truth test.
  445. *
  446. * @param (callable(TValue, TKey): bool) $callback
  447. * @param int $precision
  448. * @return float|null
  449. */
  450. public function percentage(callable $callback, int $precision = 2)
  451. {
  452. if ($this->isEmpty()) {
  453. return null;
  454. }
  455. return round(
  456. $this->filter($callback)->count() / $this->count() * 100,
  457. $precision
  458. );
  459. }
  460. /**
  461. * Get the sum of the given values.
  462. *
  463. * @param (callable(TValue): mixed)|string|null $callback
  464. * @return mixed
  465. */
  466. public function sum($callback = null)
  467. {
  468. $callback = is_null($callback)
  469. ? $this->identity()
  470. : $this->valueRetriever($callback);
  471. return $this->reduce(fn ($result, $item) => $result + $callback($item), 0);
  472. }
  473. /**
  474. * Apply the callback if the collection is empty.
  475. *
  476. * @template TWhenEmptyReturnType
  477. *
  478. * @param (callable($this): TWhenEmptyReturnType) $callback
  479. * @param (callable($this): TWhenEmptyReturnType)|null $default
  480. * @return $this|TWhenEmptyReturnType
  481. */
  482. public function whenEmpty(callable $callback, ?callable $default = null)
  483. {
  484. return $this->when($this->isEmpty(), $callback, $default);
  485. }
  486. /**
  487. * Apply the callback if the collection is not empty.
  488. *
  489. * @template TWhenNotEmptyReturnType
  490. *
  491. * @param callable($this): TWhenNotEmptyReturnType $callback
  492. * @param (callable($this): TWhenNotEmptyReturnType)|null $default
  493. * @return $this|TWhenNotEmptyReturnType
  494. */
  495. public function whenNotEmpty(callable $callback, ?callable $default = null)
  496. {
  497. return $this->when($this->isNotEmpty(), $callback, $default);
  498. }
  499. /**
  500. * Apply the callback unless the collection is empty.
  501. *
  502. * @template TUnlessEmptyReturnType
  503. *
  504. * @param callable($this): TUnlessEmptyReturnType $callback
  505. * @param (callable($this): TUnlessEmptyReturnType)|null $default
  506. * @return $this|TUnlessEmptyReturnType
  507. */
  508. public function unlessEmpty(callable $callback, ?callable $default = null)
  509. {
  510. return $this->whenNotEmpty($callback, $default);
  511. }
  512. /**
  513. * Apply the callback unless the collection is not empty.
  514. *
  515. * @template TUnlessNotEmptyReturnType
  516. *
  517. * @param callable($this): TUnlessNotEmptyReturnType $callback
  518. * @param (callable($this): TUnlessNotEmptyReturnType)|null $default
  519. * @return $this|TUnlessNotEmptyReturnType
  520. */
  521. public function unlessNotEmpty(callable $callback, ?callable $default = null)
  522. {
  523. return $this->whenEmpty($callback, $default);
  524. }
  525. /**
  526. * Filter items by the given key value pair.
  527. *
  528. * @param callable|string $key
  529. * @param mixed $operator
  530. * @param mixed $value
  531. * @return static
  532. */
  533. public function where($key, $operator = null, $value = null)
  534. {
  535. return $this->filter($this->operatorForWhere(...func_get_args()));
  536. }
  537. /**
  538. * Filter items where the value for the given key is null.
  539. *
  540. * @param string|null $key
  541. * @return static
  542. */
  543. public function whereNull($key = null)
  544. {
  545. return $this->whereStrict($key, null);
  546. }
  547. /**
  548. * Filter items where the value for the given key is not null.
  549. *
  550. * @param string|null $key
  551. * @return static
  552. */
  553. public function whereNotNull($key = null)
  554. {
  555. return $this->where($key, '!==', null);
  556. }
  557. /**
  558. * Filter items by the given key value pair using strict comparison.
  559. *
  560. * @param string $key
  561. * @param mixed $value
  562. * @return static
  563. */
  564. public function whereStrict($key, $value)
  565. {
  566. return $this->where($key, '===', $value);
  567. }
  568. /**
  569. * Filter items by the given key value pair.
  570. *
  571. * @param string $key
  572. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  573. * @param bool $strict
  574. * @return static
  575. */
  576. public function whereIn($key, $values, $strict = false)
  577. {
  578. $values = $this->getArrayableItems($values);
  579. return $this->filter(fn ($item) => in_array(data_get($item, $key), $values, $strict));
  580. }
  581. /**
  582. * Filter items by the given key value pair using strict comparison.
  583. *
  584. * @param string $key
  585. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  586. * @return static
  587. */
  588. public function whereInStrict($key, $values)
  589. {
  590. return $this->whereIn($key, $values, true);
  591. }
  592. /**
  593. * Filter items such that the value of the given key is between the given values.
  594. *
  595. * @param string $key
  596. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  597. * @return static
  598. */
  599. public function whereBetween($key, $values)
  600. {
  601. return $this->where($key, '>=', reset($values))->where($key, '<=', end($values));
  602. }
  603. /**
  604. * Filter items such that the value of the given key is not between the given values.
  605. *
  606. * @param string $key
  607. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  608. * @return static
  609. */
  610. public function whereNotBetween($key, $values)
  611. {
  612. return $this->filter(
  613. fn ($item) => data_get($item, $key) < reset($values) || data_get($item, $key) > end($values)
  614. );
  615. }
  616. /**
  617. * Filter items by the given key value pair.
  618. *
  619. * @param string $key
  620. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  621. * @param bool $strict
  622. * @return static
  623. */
  624. public function whereNotIn($key, $values, $strict = false)
  625. {
  626. $values = $this->getArrayableItems($values);
  627. return $this->reject(fn ($item) => in_array(data_get($item, $key), $values, $strict));
  628. }
  629. /**
  630. * Filter items by the given key value pair using strict comparison.
  631. *
  632. * @param string $key
  633. * @param \Illuminate\Contracts\Support\Arrayable|iterable $values
  634. * @return static
  635. */
  636. public function whereNotInStrict($key, $values)
  637. {
  638. return $this->whereNotIn($key, $values, true);
  639. }
  640. /**
  641. * Filter the items, removing any items that don't match the given type(s).
  642. *
  643. * @template TWhereInstanceOf
  644. *
  645. * @param class-string<TWhereInstanceOf>|array<array-key, class-string<TWhereInstanceOf>> $type
  646. * @return static<TKey, TWhereInstanceOf>
  647. */
  648. public function whereInstanceOf($type)
  649. {
  650. return $this->filter(function ($value) use ($type) {
  651. if (is_array($type)) {
  652. foreach ($type as $classType) {
  653. if ($value instanceof $classType) {
  654. return true;
  655. }
  656. }
  657. return false;
  658. }
  659. return $value instanceof $type;
  660. });
  661. }
  662. /**
  663. * Pass the collection to the given callback and return the result.
  664. *
  665. * @template TPipeReturnType
  666. *
  667. * @param callable($this): TPipeReturnType $callback
  668. * @return TPipeReturnType
  669. */
  670. public function pipe(callable $callback)
  671. {
  672. return $callback($this);
  673. }
  674. /**
  675. * Pass the collection into a new class.
  676. *
  677. * @template TPipeIntoValue
  678. *
  679. * @param class-string<TPipeIntoValue> $class
  680. * @return TPipeIntoValue
  681. */
  682. public function pipeInto($class)
  683. {
  684. return new $class($this);
  685. }
  686. /**
  687. * Pass the collection through a series of callable pipes and return the result.
  688. *
  689. * @param array<callable> $callbacks
  690. * @return mixed
  691. */
  692. public function pipeThrough($callbacks)
  693. {
  694. return Collection::make($callbacks)->reduce(
  695. fn ($carry, $callback) => $callback($carry),
  696. $this,
  697. );
  698. }
  699. /**
  700. * Reduce the collection to a single value.
  701. *
  702. * @template TReduceInitial
  703. * @template TReduceReturnType
  704. *
  705. * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback
  706. * @param TReduceInitial $initial
  707. * @return TReduceReturnType
  708. */
  709. public function reduce(callable $callback, $initial = null)
  710. {
  711. $result = $initial;
  712. foreach ($this as $key => $value) {
  713. $result = $callback($result, $value, $key);
  714. }
  715. return $result;
  716. }
  717. /**
  718. * Reduce the collection to multiple aggregate values.
  719. *
  720. * @param callable $callback
  721. * @param mixed ...$initial
  722. * @return array
  723. *
  724. * @throws \UnexpectedValueException
  725. */
  726. public function reduceSpread(callable $callback, ...$initial)
  727. {
  728. $result = $initial;
  729. foreach ($this as $key => $value) {
  730. $result = call_user_func_array($callback, array_merge($result, [$value, $key]));
  731. if (! is_array($result)) {
  732. throw new UnexpectedValueException(sprintf(
  733. "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.",
  734. class_basename(static::class), gettype($result)
  735. ));
  736. }
  737. }
  738. return $result;
  739. }
  740. /**
  741. * Reduce an associative collection to a single value.
  742. *
  743. * @template TReduceWithKeysInitial
  744. * @template TReduceWithKeysReturnType
  745. *
  746. * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue, TKey): TReduceWithKeysReturnType $callback
  747. * @param TReduceWithKeysInitial $initial
  748. * @return TReduceWithKeysReturnType
  749. */
  750. public function reduceWithKeys(callable $callback, $initial = null)
  751. {
  752. return $this->reduce($callback, $initial);
  753. }
  754. /**
  755. * Create a collection of all elements that do not pass a given truth test.
  756. *
  757. * @param (callable(TValue, TKey): bool)|bool|TValue $callback
  758. * @return static
  759. */
  760. public function reject($callback = true)
  761. {
  762. $useAsCallable = $this->useAsCallable($callback);
  763. return $this->filter(function ($value, $key) use ($callback, $useAsCallable) {
  764. return $useAsCallable
  765. ? ! $callback($value, $key)
  766. : $value != $callback;
  767. });
  768. }
  769. /**
  770. * Pass the collection to the given callback and then return it.
  771. *
  772. * @param callable($this): mixed $callback
  773. * @return $this
  774. */
  775. public function tap(callable $callback)
  776. {
  777. $callback($this);
  778. return $this;
  779. }
  780. /**
  781. * Return only unique items from the collection array.
  782. *
  783. * @param (callable(TValue, TKey): mixed)|string|null $key
  784. * @param bool $strict
  785. * @return static
  786. */
  787. public function unique($key = null, $strict = false)
  788. {
  789. $callback = $this->valueRetriever($key);
  790. $exists = [];
  791. return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) {
  792. if (in_array($id = $callback($item, $key), $exists, $strict)) {
  793. return true;
  794. }
  795. $exists[] = $id;
  796. });
  797. }
  798. /**
  799. * Return only unique items from the collection array using strict comparison.
  800. *
  801. * @param (callable(TValue, TKey): mixed)|string|null $key
  802. * @return static
  803. */
  804. public function uniqueStrict($key = null)
  805. {
  806. return $this->unique($key, true);
  807. }
  808. /**
  809. * Collect the values into a collection.
  810. *
  811. * @return \Illuminate\Support\Collection<TKey, TValue>
  812. */
  813. public function collect()
  814. {
  815. return new Collection($this->all());
  816. }
  817. /**
  818. * Get the collection of items as a plain array.
  819. *
  820. * @return array<TKey, mixed>
  821. */
  822. public function toArray()
  823. {
  824. return $this->map(fn ($value) => $value instanceof Arrayable ? $value->toArray() : $value)->all();
  825. }
  826. /**
  827. * Convert the object into something JSON serializable.
  828. *
  829. * @return array<TKey, mixed>
  830. */
  831. public function jsonSerialize(): array
  832. {
  833. return array_map(function ($value) {
  834. if ($value instanceof JsonSerializable) {
  835. return $value->jsonSerialize();
  836. } elseif ($value instanceof Jsonable) {
  837. return json_decode($value->toJson(), true);
  838. } elseif ($value instanceof Arrayable) {
  839. return $value->toArray();
  840. }
  841. return $value;
  842. }, $this->all());
  843. }
  844. /**
  845. * Get the collection of items as JSON.
  846. *
  847. * @param int $options
  848. * @return string
  849. */
  850. public function toJson($options = 0)
  851. {
  852. return json_encode($this->jsonSerialize(), $options);
  853. }
  854. /**
  855. * Get a CachingIterator instance.
  856. *
  857. * @param int $flags
  858. * @return \CachingIterator
  859. */
  860. public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING)
  861. {
  862. return new CachingIterator($this->getIterator(), $flags);
  863. }
  864. /**
  865. * Convert the collection to its string representation.
  866. *
  867. * @return string
  868. */
  869. public function __toString()
  870. {
  871. return $this->escapeWhenCastingToString
  872. ? e($this->toJson())
  873. : $this->toJson();
  874. }
  875. /**
  876. * Indicate that the model's string representation should be escaped when __toString is invoked.
  877. *
  878. * @param bool $escape
  879. * @return $this
  880. */
  881. public function escapeWhenCastingToString($escape = true)
  882. {
  883. $this->escapeWhenCastingToString = $escape;
  884. return $this;
  885. }
  886. /**
  887. * Add a method to the list of proxied methods.
  888. *
  889. * @param string $method
  890. * @return void
  891. */
  892. public static function proxy($method)
  893. {
  894. static::$proxies[] = $method;
  895. }
  896. /**
  897. * Dynamically access collection proxies.
  898. *
  899. * @param string $key
  900. * @return mixed
  901. *
  902. * @throws \Exception
  903. */
  904. public function __get($key)
  905. {
  906. if (! in_array($key, static::$proxies)) {
  907. throw new Exception("Property [{$key}] does not exist on this collection instance.");
  908. }
  909. return new HigherOrderCollectionProxy($this, $key);
  910. }
  911. /**
  912. * Results array of items from Collection or Arrayable.
  913. *
  914. * @param mixed $items
  915. * @return array<TKey, TValue>
  916. */
  917. protected function getArrayableItems($items)
  918. {
  919. if (is_array($items)) {
  920. return $items;
  921. }
  922. return match (true) {
  923. $items instanceof WeakMap => throw new InvalidArgumentException('Collections can not be created using instances of WeakMap.'),
  924. $items instanceof Enumerable => $items->all(),
  925. $items instanceof Arrayable => $items->toArray(),
  926. $items instanceof Traversable => iterator_to_array($items),
  927. $items instanceof Jsonable => json_decode($items->toJson(), true),
  928. $items instanceof JsonSerializable => (array) $items->jsonSerialize(),
  929. $items instanceof UnitEnum => [$items],
  930. default => (array) $items,
  931. };
  932. }
  933. /**
  934. * Get an operator checker callback.
  935. *
  936. * @param callable|string $key
  937. * @param string|null $operator
  938. * @param mixed $value
  939. * @return \Closure
  940. */
  941. protected function operatorForWhere($key, $operator = null, $value = null)
  942. {
  943. if ($this->useAsCallable($key)) {
  944. return $key;
  945. }
  946. if (func_num_args() === 1) {
  947. $value = true;
  948. $operator = '=';
  949. }
  950. if (func_num_args() === 2) {
  951. $value = $operator;
  952. $operator = '=';
  953. }
  954. return function ($item) use ($key, $operator, $value) {
  955. $retrieved = data_get($item, $key);
  956. $strings = array_filter([$retrieved, $value], function ($value) {
  957. return is_string($value) || (is_object($value) && method_exists($value, '__toString'));
  958. });
  959. if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) {
  960. return in_array($operator, ['!=', '<>', '!==']);
  961. }
  962. switch ($operator) {
  963. default:
  964. case '=':
  965. case '==': return $retrieved == $value;
  966. case '!=':
  967. case '<>': return $retrieved != $value;
  968. case '<': return $retrieved < $value;
  969. case '>': return $retrieved > $value;
  970. case '<=': return $retrieved <= $value;
  971. case '>=': return $retrieved >= $value;
  972. case '===': return $retrieved === $value;
  973. case '!==': return $retrieved !== $value;
  974. case '<=>': return $retrieved <=> $value;
  975. }
  976. };
  977. }
  978. /**
  979. * Determine if the given value is callable, but not a string.
  980. *
  981. * @param mixed $value
  982. * @return bool
  983. */
  984. protected function useAsCallable($value)
  985. {
  986. return ! is_string($value) && is_callable($value);
  987. }
  988. /**
  989. * Get a value retrieving callback.
  990. *
  991. * @param callable|string|null $value
  992. * @return callable
  993. */
  994. protected function valueRetriever($value)
  995. {
  996. if ($this->useAsCallable($value)) {
  997. return $value;
  998. }
  999. return fn ($item) => data_get($item, $value);
  1000. }
  1001. /**
  1002. * Make a function to check an item's equality.
  1003. *
  1004. * @param mixed $value
  1005. * @return \Closure(mixed): bool
  1006. */
  1007. protected function equality($value)
  1008. {
  1009. return fn ($item) => $item === $value;
  1010. }
  1011. /**
  1012. * Make a function using another function, by negating its result.
  1013. *
  1014. * @param \Closure $callback
  1015. * @return \Closure
  1016. */
  1017. protected function negate(Closure $callback)
  1018. {
  1019. return fn (...$params) => ! $callback(...$params);
  1020. }
  1021. /**
  1022. * Make a function that returns what's passed to it.
  1023. *
  1024. * @return \Closure(TValue): TValue
  1025. */
  1026. protected function identity()
  1027. {
  1028. return fn ($value) => $value;
  1029. }
  1030. }