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;
16:
17: use Traversable;
18:
19: /**
20: * Provides utility protected methods for extracting a property or column
21: * from an array or object.
22: */
23: trait ExtractTrait
24: {
25: /**
26: * Returns a callable that can be used to extract a property or column from
27: * an array or object based on a dot separated path.
28: *
29: * @param string|callable $callback A dot separated path of column to follow
30: * so that the final one can be returned or a callable that will take care
31: * of doing that.
32: * @return callable
33: */
34: protected function _propertyExtractor($callback)
35: {
36: if (!is_string($callback)) {
37: return $callback;
38: }
39:
40: $path = explode('.', $callback);
41:
42: if (strpos($callback, '{*}') !== false) {
43: return function ($element) use ($path) {
44: return $this->_extract($element, $path);
45: };
46: }
47:
48: return function ($element) use ($path) {
49: return $this->_simpleExtract($element, $path);
50: };
51: }
52:
53: /**
54: * Returns a column from $data that can be extracted
55: * by iterating over the column names contained in $path.
56: * It will return arrays for elements in represented with `{*}`
57: *
58: * @param array|\ArrayAccess $data Data.
59: * @param string[] $path Path to extract from.
60: * @return mixed
61: */
62: protected function _extract($data, $path)
63: {
64: $value = null;
65: $collectionTransform = false;
66:
67: foreach ($path as $i => $column) {
68: if ($column === '{*}') {
69: $collectionTransform = true;
70: continue;
71: }
72:
73: if ($collectionTransform &&
74: !($data instanceof Traversable || is_array($data))) {
75: return null;
76: }
77:
78: if ($collectionTransform) {
79: $rest = implode('.', array_slice($path, $i));
80:
81: return (new Collection($data))->extract($rest);
82: }
83:
84: if (!isset($data[$column])) {
85: return null;
86: }
87:
88: $value = $data[$column];
89: $data = $value;
90: }
91:
92: return $value;
93: }
94:
95: /**
96: * Returns a column from $data that can be extracted
97: * by iterating over the column names contained in $path
98: *
99: * @param array|\ArrayAccess $data Data.
100: * @param string[] $path Path to extract from.
101: * @return mixed
102: */
103: protected function _simpleExtract($data, $path)
104: {
105: $value = null;
106: foreach ($path as $column) {
107: if (!isset($data[$column])) {
108: return null;
109: }
110: $value = $data[$column];
111: $data = $value;
112: }
113:
114: return $value;
115: }
116:
117: /**
118: * Returns a callable that receives a value and will return whether or not
119: * it matches certain condition.
120: *
121: * @param array $conditions A key-value list of conditions to match where the
122: * key is the property path to get from the current item and the value is the
123: * value to be compared the item with.
124: * @return \Closure
125: */
126: protected function _createMatcherFilter(array $conditions)
127: {
128: $matchers = [];
129: foreach ($conditions as $property => $value) {
130: $extractor = $this->_propertyExtractor($property);
131: $matchers[] = function ($v) use ($extractor, $value) {
132: return $extractor($v) == $value;
133: };
134: }
135:
136: return function ($value) use ($matchers) {
137: foreach ($matchers as $match) {
138: if (!$match($value)) {
139: return false;
140: }
141: }
142:
143: return true;
144: };
145: }
146: }
147: