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