ExtractIterator.php 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Collection\Iterator;
  16. use ArrayIterator;
  17. use Cake\Collection\Collection;
  18. use Cake\Collection\CollectionInterface;
  19. /**
  20. * Creates an iterator from another iterator that extract the requested column
  21. * or property based on a path
  22. */
  23. class ExtractIterator extends Collection
  24. {
  25. /**
  26. * A callable responsible for extracting a single value for each
  27. * item in the collection.
  28. *
  29. * @var callable
  30. */
  31. protected $_extractor;
  32. /**
  33. * Creates the iterator that will return the requested property for each value
  34. * in the collection expressed in $path
  35. *
  36. * ### Example:
  37. *
  38. * Extract the user name for all comments in the array:
  39. *
  40. * ```
  41. * $items = [
  42. * ['comment' => ['body' => 'cool', 'user' => ['name' => 'Mark']],
  43. * ['comment' => ['body' => 'very cool', 'user' => ['name' => 'Renan']]
  44. * ];
  45. * $extractor = new ExtractIterator($items, 'comment.user.name'');
  46. * ```
  47. *
  48. * @param array|\Traversable $items The list of values to iterate
  49. * @param string $path a dot separated string symbolizing the path to follow
  50. * inside the hierarchy of each value so that the column can be extracted.
  51. */
  52. public function __construct($items, $path)
  53. {
  54. $this->_extractor = $this->_propertyExtractor($path);
  55. parent::__construct($items);
  56. }
  57. /**
  58. * Returns the column value defined in $path or null if the path could not be
  59. * followed
  60. *
  61. * @return mixed
  62. */
  63. public function current()
  64. {
  65. $extractor = $this->_extractor;
  66. return $extractor(parent::current());
  67. }
  68. /**
  69. * {@inheritDoc}
  70. *
  71. * We perform here some strictness analysis so that the
  72. * iterator logic is bypassed entirely.
  73. *
  74. * @return \Iterator
  75. */
  76. public function unwrap()
  77. {
  78. $iterator = $this->getInnerIterator();
  79. if ($iterator instanceof CollectionInterface) {
  80. $iterator = $iterator->unwrap();
  81. }
  82. if (get_class($iterator) !== ArrayIterator::class) {
  83. return $this;
  84. }
  85. // ArrayIterator can be traversed strictly.
  86. // Let's do that for performance gains
  87. $callback = $this->_extractor;
  88. $res = [];
  89. foreach ($iterator->getArrayCopy() as $k => $v) {
  90. $res[$k] = $callback($v);
  91. }
  92. return new ArrayIterator($res);
  93. }
  94. }