1 | <?php |
||
2 | |||
3 | namespace SilverStripe\ORM; |
||
4 | |||
5 | use ArrayAccess; |
||
6 | use Countable; |
||
7 | use IteratorAggregate; |
||
8 | |||
9 | /** |
||
10 | * Creates a map from an SS_List by defining a key column and a value column. |
||
11 | */ |
||
12 | class Map implements ArrayAccess, Countable, IteratorAggregate |
||
13 | { |
||
14 | |||
15 | protected $list, $keyField, $valueField; |
||
16 | |||
17 | /** |
||
18 | * @see Map::unshift() |
||
19 | * |
||
20 | * @var array $firstItems |
||
21 | */ |
||
22 | protected $firstItems = array(); |
||
23 | |||
24 | /** |
||
25 | * @see Map::push() |
||
26 | * |
||
27 | * @var array $lastItems |
||
28 | */ |
||
29 | protected $lastItems = array(); |
||
30 | |||
31 | /** |
||
32 | * Construct a new map around an SS_list. |
||
33 | * |
||
34 | * @param SS_List $list The list to build a map from |
||
35 | * @param string $keyField The field to use as the key of each map entry |
||
36 | * @param string $valueField The field to use as the value of each map entry |
||
37 | */ |
||
38 | public function __construct(SS_List $list, $keyField = "ID", $valueField = "Title") |
||
39 | { |
||
40 | $this->list = $list; |
||
41 | $this->keyField = $keyField; |
||
42 | $this->valueField = $valueField; |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * Set the key field for this map. |
||
47 | * |
||
48 | * @var string $keyField |
||
49 | */ |
||
50 | public function setKeyField($keyField) |
||
51 | { |
||
52 | $this->keyField = $keyField; |
||
53 | } |
||
54 | |||
55 | /** |
||
56 | * Set the value field for this map. |
||
57 | * |
||
58 | * @var string $valueField |
||
59 | */ |
||
60 | public function setValueField($valueField) |
||
61 | { |
||
62 | $this->valueField = $valueField; |
||
63 | } |
||
64 | |||
65 | /** |
||
66 | * Return an array equivalent to this map. |
||
67 | * |
||
68 | * @return array |
||
69 | */ |
||
70 | public function toArray() |
||
71 | { |
||
72 | $array = array(); |
||
73 | |||
74 | foreach ($this as $k => $v) { |
||
75 | $array[$k] = $v; |
||
76 | } |
||
77 | |||
78 | return $array; |
||
79 | } |
||
80 | |||
81 | /** |
||
82 | * Return all the keys of this map. |
||
83 | * |
||
84 | * @return array |
||
85 | */ |
||
86 | public function keys() |
||
87 | { |
||
88 | return array_keys($this->toArray()); |
||
89 | } |
||
90 | |||
91 | /** |
||
92 | * Return all the values of this map. |
||
93 | * |
||
94 | * @return array |
||
95 | */ |
||
96 | public function values() |
||
97 | { |
||
98 | return array_values($this->toArray()); |
||
99 | } |
||
100 | |||
101 | /** |
||
102 | * Unshift an item onto the start of the map. |
||
103 | * |
||
104 | * Stores the value in addition to the {@link DataQuery} for the map. |
||
105 | * |
||
106 | * @var string $key |
||
107 | * @var mixed $value |
||
108 | * @return $this |
||
109 | */ |
||
110 | public function unshift($key, $value) |
||
111 | { |
||
112 | $oldItems = $this->firstItems; |
||
113 | $this->firstItems = array( |
||
114 | $key => $value |
||
115 | ); |
||
116 | |||
117 | if ($oldItems) { |
||
0 ignored issues
–
show
|
|||
118 | $this->firstItems = $this->firstItems + $oldItems; |
||
119 | } |
||
120 | |||
121 | return $this; |
||
122 | } |
||
123 | |||
124 | /** |
||
125 | * Pushes an item onto the end of the map. |
||
126 | * |
||
127 | * @var string $key |
||
128 | * @var mixed $value |
||
129 | * @return $this |
||
130 | */ |
||
131 | public function push($key, $value) |
||
132 | { |
||
133 | $oldItems = $this->lastItems; |
||
134 | |||
135 | $this->lastItems = array( |
||
136 | $key => $value |
||
137 | ); |
||
138 | |||
139 | if ($oldItems) { |
||
0 ignored issues
–
show
The expression
$oldItems of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent. Consider making the comparison explicit by using ![]() |
|||
140 | $this->lastItems = $this->lastItems + $oldItems; |
||
141 | } |
||
142 | |||
143 | return $this; |
||
144 | } |
||
145 | |||
146 | // ArrayAccess |
||
147 | |||
148 | /** |
||
149 | * @var string $key |
||
150 | * |
||
151 | * @return boolean |
||
152 | */ |
||
153 | public function offsetExists($key) |
||
154 | { |
||
155 | if (isset($this->firstItems[$key])) { |
||
156 | return true; |
||
157 | } |
||
158 | |||
159 | if (isset($this->lastItems[$key])) { |
||
160 | return true; |
||
161 | } |
||
162 | |||
163 | $record = $this->list->find($this->keyField, $key); |
||
164 | |||
165 | return $record != null; |
||
166 | } |
||
167 | |||
168 | /** |
||
169 | * @var string $key |
||
170 | * |
||
171 | * @return mixed |
||
172 | */ |
||
173 | public function offsetGet($key) |
||
174 | { |
||
175 | if (isset($this->firstItems[$key])) { |
||
176 | return $this->firstItems[$key]; |
||
177 | } |
||
178 | |||
179 | if (isset($this->lastItems[$key])) { |
||
180 | return $this->lastItems[$key]; |
||
181 | } |
||
182 | |||
183 | $record = $this->list->find($this->keyField, $key); |
||
184 | |||
185 | if ($record) { |
||
186 | $col = $this->valueField; |
||
187 | |||
188 | return $record->$col; |
||
189 | } |
||
190 | |||
191 | return null; |
||
192 | } |
||
193 | |||
194 | /** |
||
195 | * Sets a value in the map by a given key that has been set via |
||
196 | * {@link Map::push()} or {@link Map::unshift()} |
||
197 | * |
||
198 | * Keys in the map cannot be set since these values are derived from a |
||
199 | * {@link DataQuery} instance. In this case, use {@link Map::toArray()} |
||
200 | * and manipulate the resulting array. |
||
201 | * |
||
202 | * @var string $key |
||
203 | * @var mixed $value |
||
204 | */ |
||
205 | public function offsetSet($key, $value) |
||
206 | { |
||
207 | if (isset($this->firstItems[$key])) { |
||
208 | $this->firstItems[$key] = $value; |
||
209 | } |
||
210 | |||
211 | if (isset($this->lastItems[$key])) { |
||
212 | $this->lastItems[$key] = $value; |
||
213 | } |
||
214 | |||
215 | user_error( |
||
216 | 'Map is read-only. Please use $map->push($key, $value) to append values', |
||
217 | E_USER_ERROR |
||
218 | ); |
||
219 | } |
||
220 | |||
221 | /** |
||
222 | * Removes a value in the map by a given key which has been added to the map |
||
223 | * via {@link Map::push()} or {@link Map::unshift()} |
||
224 | * |
||
225 | * Keys in the map cannot be unset since these values are derived from a |
||
226 | * {@link DataQuery} instance. In this case, use {@link Map::toArray()} |
||
227 | * and manipulate the resulting array. |
||
228 | * |
||
229 | * @var string $key |
||
230 | * @var mixed $value |
||
231 | */ |
||
232 | public function offsetUnset($key) |
||
233 | { |
||
234 | if (isset($this->firstItems[$key])) { |
||
235 | unset($this->firstItems[$key]); |
||
236 | |||
237 | return; |
||
238 | } |
||
239 | |||
240 | if (isset($this->lastItems[$key])) { |
||
241 | unset($this->lastItems[$key]); |
||
242 | |||
243 | return; |
||
244 | } |
||
245 | |||
246 | user_error( |
||
247 | "Map is read-only. Unset cannot be called on keys derived from the DataQuery", |
||
248 | E_USER_ERROR |
||
249 | ); |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Returns an Map_Iterator instance for iterating over the complete set |
||
254 | * of items in the map. |
||
255 | * |
||
256 | * Satisfies the IteratorAggreagte interface. |
||
257 | * |
||
258 | * @return Map_Iterator |
||
0 ignored issues
–
show
The type
SilverStripe\ORM\Map_Iterator was not found. Maybe you did not declare it correctly or list all dependencies?
The issue could also be caused by a filter entry in the build configuration.
If the path has been excluded in your configuration, e.g. filter:
dependency_paths: ["lib/*"]
For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths ![]() |
|||
259 | */ |
||
260 | public function getIterator() |
||
261 | { |
||
262 | $keyField = $this->keyField; |
||
263 | $valueField = $this->valueField; |
||
0 ignored issues
–
show
|
|||
264 | |||
265 | foreach ($this->firstItems as $k => $v) { |
||
266 | yield $k => $v; |
||
267 | } |
||
268 | |||
269 | foreach ($this->list as $record) { |
||
270 | if (isset($this->firstItems[$record->$keyField])) { |
||
271 | continue; |
||
272 | } |
||
273 | if (isset($this->lastItems[$record->$keyField])) { |
||
274 | continue; |
||
275 | } |
||
276 | yield $this->extractValue($record, $this->keyField) => $this->extractValue($record, $this->valueField); |
||
277 | } |
||
278 | |||
279 | foreach ($this->lastItems as $k => $v) { |
||
280 | yield $k => $v; |
||
281 | } |
||
282 | } |
||
283 | |||
284 | /** |
||
285 | * Extracts a value from an item in the list, where the item is either an |
||
286 | * object or array. |
||
287 | * |
||
288 | * @param array|object $item |
||
289 | * @param string $key |
||
290 | * @return mixed |
||
291 | */ |
||
292 | protected function extractValue($item, $key) |
||
293 | { |
||
294 | if (is_object($item)) { |
||
295 | if (method_exists($item, 'hasMethod') && $item->hasMethod($key)) { |
||
296 | return $item->{$key}(); |
||
297 | } |
||
298 | return $item->{$key}; |
||
299 | } else { |
||
300 | if (array_key_exists($key, $item)) { |
||
301 | return $item[$key]; |
||
302 | } |
||
303 | } |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Returns the count of items in the list including the additional items set |
||
308 | * through {@link Map::push()} and {@link Map::unshift}. |
||
309 | * |
||
310 | * @return int |
||
311 | */ |
||
312 | public function count() |
||
313 | { |
||
314 | return $this->list->count() + |
||
315 | count($this->firstItems) + |
||
316 | count($this->lastItems); |
||
317 | } |
||
318 | } |
||
319 |
This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.
Consider making the comparison explicit by using
empty(..)
or! empty(...)
instead.