Test Failed
Push — master ( 2fbbce...c5a571 )
by Tomáš
02:30
created

BaseContainer::whereId()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
c 0
b 0
f 0
nc 1
nop 1
dl 0
loc 4
ccs 4
cts 4
cp 1
crap 1
rs 10
1
<?php
2
3
4
namespace TournamentGenerator\Containers;
5
6
use Closure;
7
use Countable;
8
use Exception;
9
use Iterator;
10
use TournamentGenerator\Helpers\Sorter\BaseSorter;
11
12
/**
13
 * Class BaseContainer
14
 *
15
 * Container is a helper class for a tree-like structure. It can be used to create a hierarchy and store objects.
16
 *
17
 * @package TournamentGenerator\Containers
18
 * @author  Tomáš Vojík <[email protected]>
19
 * @since   0.4
20
 */
21
class BaseContainer implements Countable, Iterator
22
{
23
24
	/** @var string|int Identifier */
25
	public $id;
26
	/** @var BaseContainer[] Direct child containers */
27
	protected array $children = [];
28
	/** @var BaseContainer|null Parent container reference */
29
	protected ?BaseContainer $parent;
30
	/** @var array Any value that the container holds */
31
	protected array $values = [];
32
33
	/** @var int Current iterator index */
34
	protected int $currentIndex = 0;
35
36
	/**
37
	 * BaseContainer constructor.
38
	 *
39
	 * @param string|int $id
40
	 */
41 273
	public function __construct($id, BaseContainer $parent = null) {
42 273
		$this->id = $id;
43 273
		$this->parent = $parent;
44 273
	}
45
46
	/**
47
	 * Create a new container from array
48
	 *
49
	 * @param array $data
50
	 *
51
	 * @return BaseContainer
52
	 * @throws Exception
53
	 */
54 4
	public static function fromArray(array $data) : BaseContainer {
55 4
		$container = new self(0);
56 4
		$container->insertFlat(...$data);
57 4
		return $container;
58
	}
59
60
	/**
61
	 * Insert a value into container
62
	 *
63
	 * @param array $values Any value to insert into container
64
	 *
65
	 * @return $this
66
	 * @throws Exception
67
	 */
68 4
	protected function insertFlat(...$values) : BaseContainer {
69 4
		foreach ($values as $value) {
70 4
			$this->values[] = $value;
71
		}
72 4
		return $this;
73
	}
74
75
	/**
76
	 * Returns the value count
77
	 *
78
	 * @return int
79
	 */
80 42
	public function count() : int {
81 42
		return count($this->get());
82
	}
83
84
	/**
85
	 * Get all values from the container - including child nodes
86
	 *
87
	 * @return array All values
88
	 */
89 137
	public function get() : array {
90 137
		if (count($this->children) > 0) {
91 73
			$values = [$this->values];
92 73
			foreach ($this->children as $child) {
93 73
				$values[] = $child->get();
94
			}
95 73
			return array_merge(...$values);
96
		}
97 137
		return $this->values;
98
	}
99
100
	/**
101
	 * Get all top-level values from the container
102
	 *
103
	 * @return array All values
104
	 */
105 3
	public function getTopLevel() : array {
106 3
		return $this->values;
107
	}
108
109 1
	public function getTopLevelQuery() : ContainerQuery {
110 1
		return new ContainerQuery($this, true);
111
	}
112
113 5
	public function getQuery() : ContainerQuery {
114 5
		return new ContainerQuery($this);
115
	}
116
117
	/**
118
	 * Get the current value
119
	 *
120
	 * @return mixed
121
	 */
122 2
	public function current() {
123 2
		return $this->get()[$this->currentIndex];
124
	}
125
126
	/**
127
	 * Move pointer to next
128
	 */
129 2
	public function next() : void {
130 2
		++$this->currentIndex;
131 2
	}
132
133
	/**
134
	 * Return the current key
135
	 *
136
	 * @return int
137
	 */
138 1
	public function key() : int {
139 1
		return $this->currentIndex;
140
	}
141
142
	/**
143
	 * Check if the current value exists
144
	 *
145
	 * @return bool
146
	 */
147 2
	public function valid() : bool {
148 2
		return isset($this->get()[$this->currentIndex]);
149
	}
150
151
	/**
152
	 * Rewind the iterator
153
	 */
154 2
	public function rewind() : void {
155 2
		$this->currentIndex = 0;
156 2
	}
157
158
	/**
159
	 * Insert a value into container
160
	 *
161
	 * @param array $values Any value to insert into container
162
	 *
163
	 * @post If the value has a container -> add it to the hierarchy
164
	 *
165
	 * @return $this
166
	 * @throws Exception
167
	 */
168 246
	public function insert(...$values) : BaseContainer {
169 246
		foreach ($values as $value) {
170 246
			$this->values[] = $value;
171 246
			if (is_object($value) && method_exists($value, 'getContainer')) {
172 143
				$this->addChild($value->getContainer());
173
			}
174
		}
175 246
		return $this;
176
	}
177
178
	/**
179
	 * Adds a child container
180
	 *
181
	 * @param BaseContainer[] $containers
182
	 *
183
	 * @return $this
184
	 * @post Parent container is set for the added children
185
	 * @throws Exception
186
	 */
187 148
	public function addChild(BaseContainer ...$containers) : BaseContainer {
188 148
		foreach ($containers as $container) {
189 148
			if (!isset($this->children[$container->id])) {
190 148
				$container->setParent($this);
191 148
				$this->children[$container->id] = $container;
192
			}
193
		}
194 148
		return $this;
195
	}
196
197
	/**
198
	 * Gets all ids of the leaf containers
199
	 *
200
	 * @return string[]|int[]
201
	 */
202 50
	public function getLeafIds() : array {
203 50
		if (count($this->children) > 0) {
204 11
			$ids = [];
205 11
			foreach ($this->children as $child) {
206 11
				$ids[] = $child->getLeafIds();
207
			}
208 11
			return array_merge(...$ids);
209
		}
210 50
		return [$this->id];
211
	}
212
213
	/**
214
	 * Add a filter callback
215
	 *
216
	 * @param Closure $callback
217
	 *
218
	 * @return ContainerQuery
219
	 */
220 5
	public function filter(Closure $callback) : ContainerQuery {
221 5
		$query = new ContainerQuery($this);
222 5
		$query->filter($callback);
223 5
		return $query;
224
	}
225
226
	/**
227
	 * Filter results to only contain those with a specific ID
228
	 *
229
	 * @param string|int $id
230
	 *
231
	 * @return ContainerQuery
232
	 */
233 1
	public function whereId($id) : ContainerQuery {
234 1
		$query = new ContainerQuery($this);
235 1
		$query->whereId($id);
236 1
		return $query;
237
	}
238
239
	/**
240
	 * Sort a result using a callback - maintaining the index association
241
	 *
242
	 * @param Closure $callback
243
	 *
244
	 * @return ContainerQuery
245
	 */
246
	public function sort(Closure $callback) : ContainerQuery {
247
		$query = new ContainerQuery($this);
248 1
		$query->sort($callback);
249 1
		return $query;
250 1
	}
251 1
252
	/**
253
	 * Sort a result set by a given property
254
	 *
255
	 * @warning Sort callback has a priority.
256
	 *
257
	 * @param string $property
258
	 *
259 48
	 * @return ContainerQuery
260 48
	 */
261 48
	public function sortBy(string $property) : ContainerQuery {
262 48
		$query = new ContainerQuery($this);
263
		$query->sortBy($property);
264
		return $query;
265
	}
266
267
	/**
268
	 * @param BaseSorter $sorter
269
	 *
270 119
	 * @return ContainerQuery
271 119
	 */
272 119
	public function addSorter(BaseSorter $sorter) : ContainerQuery {
273 119
		$query = new ContainerQuery($this);
274
		$query->addSorter($sorter);
275
		return $query;
276
	}
277
278
	/**
279
	 * Get only unique values
280
	 *
281
	 * @return ContainerQuery
282
	 */
283
	public function unique() : ContainerQuery {
284
		$query = new ContainerQuery($this);
285 5
		$query->unique();
286 5
		return $query;
287 5
	}
288 5
289
290
	/**
291
	 * Pluck a specific key from all values
292
	 *
293
	 * @param string $property Property, array key or method to extract from values
294
	 *
295
	 * @return ContainerQuery
296
	 * @throws Exception
297 4
	 */
298 4
	public function only(string $property) : ContainerQuery {
299 4
		$query = new ContainerQuery($this);
300 4
		$query->only($property);
301
		return $query;
302
	}
303
304
	/**
305
	 * Get only the object's ids
306
	 *
307
	 * @return ContainerQuery
308
	 * @throws Exception
309 2
	 */
310 2
	public function ids() : ContainerQuery {
311
		$query = new ContainerQuery($this);
312
		$query->ids();
313
		return $query;
314
	}
315
316
	/**
317
	 * Get a parent container
318
	 *
319
	 * @return BaseContainer|null
320
	 * @since 0.5
321
	 */
322 149
	public function getParent() : ?BaseContainer {
323 149
		return $this->parent;
324 1
	}
325
326 148
	/**
327 148
	 * Set a container's parent
328
	 *
329
	 * @param BaseContainer|null $parent
330
	 *
331
	 * @return BaseContainer
332
	 * @throws Exception
333
	 * @since 0.5
334
	 */
335
	public function setParent(BaseContainer $parent) : BaseContainer {
336
		if ($parent !== $this->parent && !is_null($this->parent)) {
337
			throw new Exception('Parent container can only be set once!');
338
		}
339
		$this->parent = $parent;
340
		return $this;
341
	}
342
}