Completed
Push — master ( a743c4...8e8354 )
by Tomáš
02:35
created

BaseContainer::fromArray()   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
nc 1
nop 1
dl 0
loc 4
ccs 4
cts 4
cp 1
crap 1
rs 10
c 0
b 0
f 0
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
	 * Create a new container from array
38
	 *
39
	 * @param array $data
40
	 *
41
	 * @return BaseContainer
42
	 * @throws Exception
43
	 */
44 4
	public static function fromArray(array $data) : BaseContainer {
45 4
		$container = new self(0);
46 4
		$container->insertFlat(...$data);
47 4
		return $container;
48
	}
49
50
	/**
51
	 * BaseContainer constructor.
52
	 *
53
	 * @param string|int $id
54
	 */
55 271
	public function __construct($id, BaseContainer $parent = null) {
56 271
		$this->id = $id;
57 271
		$this->parent = $parent;
58 271
	}
59
60
	/**
61
	 * Returns the value count
62
	 *
63
	 * @return int
64
	 */
65 42
	public function count() : int {
66 42
		return count($this->get());
67
	}
68
69
	/**
70
	 * Get all values from the container - including child nodes
71
	 *
72
	 * @return array All values
73
	 */
74 137
	public function get() : array {
75 137
		if (count($this->children) > 0) {
76 73
			$values = [$this->values];
77 73
			foreach ($this->children as $child) {
78 73
				$values[] = $child->get();
79
			}
80 73
			return array_merge(...$values);
81
		}
82 137
		return $this->values;
83
	}
84
85
	/**
86
	 * Get all top-level values from the container
87
	 *
88
	 * @return array All values
89
	 */
90 3
	public function getTopLevel() : array {
91 3
		return $this->values;
92
	}
93
94 1
	public function getTopLevelQuery() : ContainerQuery {
95 1
		return new ContainerQuery($this, true);
96
	}
97
98 5
	public function getQuery() : ContainerQuery {
99 5
		return new ContainerQuery($this);
100
	}
101
102
	/**
103
	 * Get the current value
104
	 *
105
	 * @return mixed
106
	 */
107 2
	public function current() {
108 2
		return $this->get()[$this->currentIndex];
109
	}
110
111
	/**
112
	 * Move pointer to next
113
	 */
114 2
	public function next() : void {
115 2
		++$this->currentIndex;
116 2
	}
117
118
	/**
119
	 * Return the current key
120
	 *
121
	 * @return int
122
	 */
123 1
	public function key() : int {
124 1
		return $this->currentIndex;
125
	}
126
127
	/**
128
	 * Check if the current value exists
129
	 *
130
	 * @return bool
131
	 */
132 2
	public function valid() : bool {
133 2
		return isset($this->get()[$this->currentIndex]);
134
	}
135
136
	/**
137
	 * Rewind the iterator
138
	 */
139 2
	public function rewind() : void {
140 2
		$this->currentIndex = 0;
141 2
	}
142
143
	/**
144
	 * Insert a value into container
145
	 *
146
	 * @param array $values Any value to insert into container
147
	 *
148
	 * @post If the value has a container -> add it to the hierarchy
149
	 *
150
	 * @return $this
151
	 * @throws Exception
152
	 */
153 244
	public function insert(...$values) : BaseContainer {
154 244
		foreach ($values as $value) {
155 244
			$this->values[] = $value;
156 244
			if (is_object($value) && method_exists($value, 'getContainer')) {
157 143
				$this->addChild($value->getContainer());
158
			}
159
		}
160 244
		return $this;
161
	}
162
163
	/**
164
	 * Insert a value into container
165
	 *
166
	 * @param array $values Any value to insert into container
167
	 *
168
	 * @return $this
169
	 * @throws Exception
170
	 */
171 4
	protected function insertFlat(...$values) : BaseContainer {
172 4
		foreach ($values as $value) {
173 4
			$this->values[] = $value;
174
		}
175 4
		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
	 * Sort a result using a callback - maintaining the index association
228
	 *
229
	 * @param Closure $callback
230
	 *
231
	 * @return ContainerQuery
232
	 */
233 1
	public function sort(Closure $callback) : ContainerQuery {
234 1
		$query = new ContainerQuery($this);
235 1
		$query->sort($callback);
236 1
		return $query;
237
	}
238
239
	/**
240
	 * Sort a result set by a given property
241
	 *
242
	 * @warning Sort callback has a priority.
243
	 *
244
	 * @param string $property
245
	 *
246
	 * @return ContainerQuery
247
	 */
248 1
	public function sortBy(string $property) : ContainerQuery {
249 1
		$query = new ContainerQuery($this);
250 1
		$query->sortBy($property);
251 1
		return $query;
252
	}
253
254
	/**
255
	 * @param BaseSorter $sorter
256
	 *
257
	 * @return ContainerQuery
258
	 */
259 48
	public function addSorter(BaseSorter $sorter) : ContainerQuery {
260 48
		$query = new ContainerQuery($this);
261 48
		$query->addSorter($sorter);
262 48
		return $query;
263
	}
264
265
	/**
266
	 * Get only unique values
267
	 *
268
	 * @return ContainerQuery
269
	 */
270 119
	public function unique() : ContainerQuery {
271 119
		$query = new ContainerQuery($this);
272 119
		$query->unique();
273 119
		return $query;
274
	}
275
276
277
278
	/**
279
	 * Pluck a specific key from all values
280
	 *
281
	 * @param string $property Property, array key or method to extract from values
282
	 *
283
	 * @return ContainerQuery
284
	 * @throws Exception
285
	 */
286 5
	public function only(string $property) : ContainerQuery {
287 5
		$query = new ContainerQuery($this);
288 5
		$query->only($property);
289 5
		return $query;
290
	}
291
292
	/**
293
	 * Get only the object's ids
294
	 *
295
	 * @return ContainerQuery
296
	 * @throws Exception
297
	 */
298 4
	public function ids() : ContainerQuery {
299 4
		$query = new ContainerQuery($this);
300 4
		$query->ids();
301 4
		return $query;
302
	}
303
304
	/**
305
	 * Get a parent container
306
	 *
307
	 * @return BaseContainer|null
308
	 * @since 0.5
309
	 */
310 2
	public function getParent() : ?BaseContainer {
311 2
		return $this->parent;
312
	}
313
314
	/**
315
	 * Set a container's parent
316
	 *
317
	 * @param BaseContainer|null $parent
318
	 *
319
	 * @return BaseContainer
320
	 * @throws Exception
321
	 * @since 0.5
322
	 */
323 149
	public function setParent(BaseContainer $parent) : BaseContainer {
324 149
		if ($parent !== $this->parent && !is_null($this->parent)) {
325 1
			throw new Exception('Parent container can only be set once!');
326
		}
327 148
		$this->parent = $parent;
328 148
		return $this;
329
	}
330
}