BaseContainer::getTopLevelQuery()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 1
c 1
b 0
f 0
dl 0
loc 2
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
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 345
	public function __construct($id, BaseContainer $parent = null) {
42 345
		$this->id = $id;
43 345
		$this->parent = $parent;
44 345
	}
45
46
	/**
47
	 * Create a new container from array
48
	 *
49
	 * @param array $data
50
	 *
51
	 * @return BaseContainer
52
	 * @throws Exception
53
	 */
54 21
	public static function fromArray(array $data) : BaseContainer {
55 21
		$container = new self(0);
56 21
		$container->insertFlat(...$data);
57 21
		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 22
	public function insertFlat(...$values) : BaseContainer {
69 22
		foreach ($values as $value) {
70 22
			$this->values[] = $value;
71
		}
72 22
		return $this;
73
	}
74
75
	/**
76
	 * Returns the value count
77
	 *
78
	 * @return int
79
	 */
80 64
	public function count() : int {
81 64
		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 190
	public function get() : array {
90 190
		if (count($this->children) > 0) {
91 105
			$values = [$this->values];
92 105
			foreach ($this->children as $child) {
93 105
				$values[] = $child->get();
94
			}
95 105
			return array_merge(...$values);
96
		}
97 190
		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 23
	public function getQuery() : ContainerQuery {
114 23
		return new ContainerQuery($this);
115
	}
116
117
	/**
118
	 * Get the current value
119
	 *
120
	 * @return mixed
121
	 */
122 3
	public function current() {
123 3
		return $this->get()[$this->currentIndex];
124
	}
125
126
	/**
127
	 * Move pointer to next
128
	 *
129
	 * @infection-ignore-all
130
	 */
131 3
	public function next() : void {
132 3
		++$this->currentIndex;
133 3
	}
134
135
	/**
136
	 * Return the current key
137
	 *
138
	 * @return int
139
	 */
140 1
	public function key() : int {
141 1
		return $this->currentIndex;
142
	}
143
144
	/**
145
	 * Check if the current value exists
146
	 *
147
	 * @return bool
148
	 */
149 3
	public function valid() : bool {
150 3
		return isset($this->get()[$this->currentIndex]);
151
	}
152
153
	/**
154
	 * Rewind the iterator
155
	 *
156
	 * @infection-ignore-all
157
	 */
158 3
	public function rewind() : void {
159 3
		$this->currentIndex = 0;
160 3
	}
161
162
	/**
163
	 * Insert a value into container
164
	 *
165
	 * @param array $values Any value to insert into container
166
	 *
167
	 * @post If the value has a container -> add it to the hierarchy
168
	 *
169
	 * @return $this
170
	 * @throws Exception
171
	 */
172 295
	public function insert(...$values) : BaseContainer {
173 295
		foreach ($values as $value) {
174 295
			$this->values[] = $value;
175 295
			if (is_object($value) && method_exists($value, 'getContainer')) {
176 180
				$this->addChild($value->getContainer());
177
			}
178
		}
179 295
		return $this;
180
	}
181
182
	/**
183
	 * Adds a child container
184
	 *
185
	 * @param BaseContainer[] $containers
186
	 *
187
	 * @return $this
188
	 * @post Parent container is set for the added children
189
	 * @throws Exception
190
	 */
191 185
	public function addChild(BaseContainer ...$containers) : BaseContainer {
192 185
		foreach ($containers as $container) {
193 185
			if (!isset($this->children[$container->id])) {
194 185
				$container->setParent($this);
195 185
				$this->children[$container->id] = $container;
196
			}
197
		}
198 185
		return $this;
199
	}
200
201
	/**
202
	 * Gets all ids of the leaf containers
203
	 *
204
	 * @return string[]|int[]
205
	 */
206 80
	public function getLeafIds() : array {
207 80
		if (count($this->children) > 0) {
208 68
			$ids = [];
209 68
			foreach ($this->children as $child) {
210 68
				$ids[] = $child->getLeafIds();
211
			}
212 68
			return array_merge(...$ids);
213
		}
214 80
		return [$this->id];
215
	}
216
217
	/**
218
	 * Add a filter callback
219
	 *
220
	 * @param Closure $callback
221
	 *
222
	 * @return ContainerQuery
223
	 */
224 6
	public function filter(Closure $callback) : ContainerQuery {
225 6
		$query = new ContainerQuery($this);
226 6
		$query->filter($callback);
227 6
		return $query;
228
	}
229
230
	/**
231
	 * Filter results to only contain those with a specific ID
232
	 *
233
	 * @param string|int $id
234
	 *
235
	 * @return ContainerQuery
236
	 */
237 13
	public function whereId($id) : ContainerQuery {
238 13
		$query = new ContainerQuery($this);
239 13
		$query->whereId($id);
240 13
		return $query;
241
	}
242
243
	/**
244
	 * Sort a result using a callback - maintaining the index association
245
	 *
246
	 * @param Closure|null $callback
247
	 *
248
	 * @return ContainerQuery
249
	 */
250 2
	public function sort(?Closure $callback = null) : ContainerQuery {
251 2
		$query = new ContainerQuery($this);
252 2
		$query->sort($callback);
253 2
		return $query;
254
	}
255
256
	/**
257
	 * Sort a result set by a given property
258
	 *
259
	 * @warning Sort callback has a priority.
260
	 *
261
	 * @param string $property
262
	 *
263
	 * @return ContainerQuery
264
	 */
265 3
	public function sortBy(string $property) : ContainerQuery {
266 3
		$query = new ContainerQuery($this);
267 3
		$query->sortBy($property);
268 3
		return $query;
269
	}
270
271
	/**
272
	 * @param BaseSorter $sorter
273
	 *
274
	 * @return ContainerQuery
275
	 */
276 79
	public function addSorter(BaseSorter $sorter) : ContainerQuery {
277 79
		$query = new ContainerQuery($this);
278 79
		$query->addSorter($sorter);
279 79
		return $query;
280
	}
281
282
	/**
283
	 * Get only unique values
284
	 *
285
	 * @return ContainerQuery
286
	 */
287 143
	public function unique() : ContainerQuery {
288 143
		$query = new ContainerQuery($this);
289 143
		$query->unique();
290 143
		return $query;
291
	}
292
293
294
	/**
295
	 * Pluck a specific key from all values
296
	 *
297
	 * @param string $property Property, array key or method to extract from values
298
	 *
299
	 * @return ContainerQuery
300
	 * @throws Exception
301
	 */
302 6
	public function only(string $property) : ContainerQuery {
303 6
		$query = new ContainerQuery($this);
304 6
		$query->only($property);
305 6
		return $query;
306
	}
307
308
	/**
309
	 * Get only the object's ids
310
	 *
311
	 * @return ContainerQuery
312
	 * @throws Exception
313
	 */
314 8
	public function ids() : ContainerQuery {
315 8
		$query = new ContainerQuery($this);
316 8
		$query->ids();
317 8
		return $query;
318
	}
319
320
	/**
321
	 * Get a parent container
322
	 *
323
	 * @return BaseContainer|null
324
	 * @since 0.5
325
	 */
326 2
	public function getParent() : ?BaseContainer {
327 2
		return $this->parent;
328
	}
329
330
	/**
331
	 * Set a container's parent
332
	 *
333
	 * @param BaseContainer|null $parent
334
	 *
335
	 * @return BaseContainer
336
	 * @throws Exception
337
	 * @since 0.5
338
	 */
339 186
	public function setParent(BaseContainer $parent) : BaseContainer {
340 186
		if ($parent !== $this->parent && !is_null($this->parent)) {
341 1
			throw new Exception('Parent container can only be set once!');
342
		}
343 185
		$this->parent = $parent;
344 185
		return $this;
345
	}
346
}