1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
|
4
|
|
|
namespace TournamentGenerator\Import; |
5
|
|
|
|
6
|
|
|
use Exception; |
7
|
|
|
use JsonException; |
8
|
|
|
use TournamentGenerator\Base; |
9
|
|
|
use TournamentGenerator\Category; |
10
|
|
|
use TournamentGenerator\Group; |
11
|
|
|
use TournamentGenerator\Interfaces\WithGames; |
12
|
|
|
use TournamentGenerator\Interfaces\WithSkipSetters; |
13
|
|
|
use TournamentGenerator\Round; |
14
|
|
|
use TournamentGenerator\Team; |
15
|
|
|
use TournamentGenerator\TeamFilter; |
16
|
|
|
use TournamentGenerator\Tournament; |
17
|
|
|
|
18
|
|
|
/** |
19
|
|
|
* Basic importer |
20
|
|
|
* |
21
|
|
|
* Importer uses exported data and creates a new tournament objects from it. |
22
|
|
|
* |
23
|
|
|
* @package TournamentGenerator\Import |
24
|
|
|
* @author Tomáš Vojík <[email protected]> |
25
|
|
|
* @since 0.5 |
26
|
|
|
*/ |
27
|
|
|
class Importer |
28
|
|
|
{ |
29
|
|
|
/** @var array List of $categoryId => $parent */ |
30
|
|
|
protected static array $categories = []; |
31
|
|
|
/** @var array List of $roundId => $parent */ |
32
|
|
|
protected static array $rounds = []; |
33
|
|
|
/** @var array List of $groupId => $parent */ |
34
|
|
|
protected static array $groups = []; |
35
|
|
|
/** @var array List of $teamId => $parent */ |
36
|
|
|
protected static array $teams = []; |
37
|
|
|
/** @var array List of $gameId => $parent */ |
38
|
|
|
protected static array $games = []; |
39
|
|
|
|
40
|
|
|
/** @var Base|null $root Root object - returned from import */ |
41
|
|
|
protected static ?Base $root = null; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* Processes a JSON input and creates necessary objects from it |
45
|
|
|
* |
46
|
|
|
* @param string $data |
47
|
|
|
* |
48
|
|
|
* @return Base |
49
|
|
|
* @throws JsonException |
50
|
|
|
* @throws InvalidImportDataException |
51
|
|
|
* @see Importer::import() |
52
|
|
|
* |
53
|
|
|
*/ |
54
|
35 |
|
public static function importJson(string $data) : ?Base { |
55
|
35 |
|
return self::import(json_decode($data, true, 512, JSON_THROW_ON_ERROR)); |
|
|
|
|
56
|
|
|
} |
57
|
|
|
|
58
|
|
|
/** |
59
|
|
|
* Processes an input array of data and creates necessary objects from it |
60
|
|
|
* |
61
|
|
|
* @param array $data |
62
|
|
|
* |
63
|
|
|
* @return Base Imported root object or null if nothing was created |
64
|
|
|
* |
65
|
|
|
* @throws InvalidImportDataException |
66
|
|
|
* @throws Exception |
67
|
|
|
*/ |
68
|
37 |
|
public static function import(array $data) : ?Base { |
69
|
|
|
// Validate data |
70
|
37 |
|
ImportValidator::validate($data); |
71
|
|
|
|
72
|
|
|
// Groups for parent logging |
73
|
|
|
// This allows setting a parent to other objects. The latest parent in hierarchy. |
74
|
37 |
|
self::$categories = []; |
75
|
37 |
|
self::$rounds = []; |
76
|
37 |
|
self::$groups = []; |
77
|
37 |
|
self::$teams = []; |
78
|
37 |
|
self::$games = []; |
79
|
|
|
|
80
|
|
|
// Reset root |
81
|
37 |
|
self::$root = null; |
82
|
|
|
|
83
|
|
|
// Helper array - $id => $object reference |
84
|
37 |
|
$allGroups = []; |
85
|
37 |
|
$allTeams = []; |
86
|
|
|
|
87
|
|
|
// Try setting up the tournament object |
88
|
37 |
|
self::createTournament((array) ($data['tournament'] ?? [])); |
89
|
|
|
|
90
|
|
|
// Try setting up all category objects |
91
|
37 |
|
self::createCategories($data['categories'] ?? []); |
92
|
|
|
|
93
|
|
|
// Try setting up all round objects |
94
|
37 |
|
self::createRounds($data['rounds'] ?? []); |
95
|
|
|
|
96
|
|
|
// Try setting up all group objects |
97
|
37 |
|
self::createGroups($data['groups'] ?? [], $allGroups); |
98
|
|
|
|
99
|
|
|
// Try setting up all progression objects |
100
|
37 |
|
self::createProgressions($data['progressions'] ?? [], $allGroups); |
101
|
|
|
|
102
|
|
|
// Try setting up all team objects |
103
|
37 |
|
self::createTeams($data['teams'] ?? [], $allTeams); |
104
|
|
|
|
105
|
|
|
// Try setting up all game objects |
106
|
37 |
|
self::createGames($data['games'] ?? [], $allTeams); |
107
|
|
|
|
108
|
37 |
|
return self::$root; |
109
|
|
|
} |
110
|
|
|
|
111
|
|
|
/** |
112
|
|
|
* Creates a tournament object |
113
|
|
|
* |
114
|
|
|
* @param array $setting |
115
|
|
|
*/ |
116
|
37 |
|
protected static function createTournament(array $setting) : void { |
117
|
37 |
|
if (!empty($setting)) { |
118
|
|
|
// Check tournament type (can be a preset) |
119
|
14 |
|
if (empty($setting['type']) || $setting['type'] === 'general') { |
120
|
11 |
|
$tournament = new Tournament(); |
121
|
|
|
} |
122
|
|
|
else { |
123
|
3 |
|
$tournament = new $setting['type']; |
124
|
|
|
} |
125
|
14 |
|
self::$root = $tournament; // If set - Tournament is always root |
126
|
|
|
|
127
|
14 |
|
self::setTournament($tournament, $setting); |
128
|
14 |
|
self::logAllIds($setting, $tournament); |
129
|
|
|
} |
130
|
37 |
|
} |
131
|
|
|
|
132
|
|
|
/** |
133
|
|
|
* Setup a tournament with all its settings |
134
|
|
|
* |
135
|
|
|
* @param Tournament $tournament |
136
|
|
|
* @param array $setting |
137
|
|
|
*/ |
138
|
14 |
|
protected static function setTournament(Tournament $tournament, array $setting) : void { |
139
|
14 |
|
foreach ($setting as $key => $value) { |
140
|
14 |
|
switch ($key) { |
141
|
14 |
|
case 'name': |
142
|
14 |
|
$tournament->setName($value); |
143
|
14 |
|
break; |
144
|
14 |
|
case 'skip': |
145
|
10 |
|
$tournament->setSkip($value); |
146
|
10 |
|
break; |
147
|
14 |
|
case 'timing': |
148
|
10 |
|
self::setTiming($tournament, (array) $value); |
149
|
10 |
|
break; |
150
|
|
|
} |
151
|
|
|
} |
152
|
14 |
|
} |
153
|
|
|
|
154
|
|
|
/** |
155
|
|
|
* Set timing setting to a tournament object |
156
|
|
|
* |
157
|
|
|
* @param Tournament $object |
158
|
|
|
* @param array $setting |
159
|
|
|
*/ |
160
|
10 |
|
protected static function setTiming(Tournament $object, array $setting) : void { |
161
|
10 |
|
foreach ($setting as $key2 => $value2) { |
162
|
10 |
|
switch ($key2) { |
163
|
10 |
|
case 'play': |
164
|
10 |
|
$object->setPlay($value2); |
165
|
10 |
|
break; |
166
|
9 |
|
case 'gameWait': |
167
|
9 |
|
$object->setGameWait($value2); |
168
|
9 |
|
break; |
169
|
9 |
|
case 'categoryWait': |
170
|
9 |
|
$object->setCategoryWait($value2); |
171
|
9 |
|
break; |
172
|
9 |
|
case 'roundWait': |
173
|
9 |
|
$object->setRoundWait($value2); |
174
|
9 |
|
break; |
175
|
|
|
} |
176
|
|
|
} |
177
|
10 |
|
} |
178
|
|
|
|
179
|
|
|
/** |
180
|
|
|
* Log all set ids and objects into helper arrays |
181
|
|
|
* |
182
|
|
|
* Adds an $id => $object pair for each id and group. Sets an game autoincrement value to the lowest possible game id if the object is root. |
183
|
|
|
* |
184
|
|
|
* @param array $setting Object's settings |
185
|
|
|
* @param Base $object Object that is logged |
186
|
|
|
* |
187
|
|
|
* @see Importer::addIds() |
188
|
|
|
*/ |
189
|
34 |
|
protected static function logAllIds(array $setting, Base $object) : void { |
190
|
34 |
|
self::addIds(self::$categories, $object, $setting['categories'] ?? []); |
191
|
34 |
|
self::addIds(self::$rounds, $object, $setting['rounds'] ?? []); |
192
|
34 |
|
self::addIds(self::$groups, $object, $setting['groups'] ?? []); |
193
|
34 |
|
self::addIds(self::$teams, $object, $setting['teams'] ?? [], true); |
194
|
34 |
|
self::addIds(self::$games, $object, $setting['games'] ?? []); |
195
|
|
|
/** @noinspection NotOptimalIfConditionsInspection */ |
196
|
34 |
|
if (self::$root === $object && !empty($setting['games']) && $object instanceof WithGames) { |
197
|
9 |
|
$object->getGameContainer()->setAutoIncrement(min($setting['games'])); |
198
|
|
|
} |
199
|
34 |
|
} |
200
|
|
|
|
201
|
|
|
/** |
202
|
|
|
* Log an object as parent to other object |
203
|
|
|
* |
204
|
|
|
* Adds an $id => $object pairs for each id into $group. |
205
|
|
|
* |
206
|
|
|
* @param array $group Group to log the object into |
207
|
|
|
* @param Base $object Object to log |
208
|
|
|
* @param array $ids List of child object ids |
209
|
|
|
*/ |
210
|
34 |
|
protected static function addIds(array &$group, Base $object, array $ids, bool $multiple = false) : void { |
211
|
34 |
|
foreach ($ids as $id) { |
212
|
16 |
|
if ($multiple) { |
213
|
11 |
|
if (!isset($group[$id])) { |
214
|
11 |
|
$group[$id] = []; |
215
|
|
|
} |
216
|
11 |
|
$group[$id][] = $object; |
217
|
11 |
|
continue; |
218
|
|
|
} |
219
|
16 |
|
$group[$id] = $object; |
220
|
|
|
} |
221
|
34 |
|
} |
222
|
|
|
|
223
|
|
|
/** |
224
|
|
|
* Create category objects |
225
|
|
|
* |
226
|
|
|
* @param array $categories |
227
|
|
|
*/ |
228
|
37 |
|
protected static function createCategories(array $categories) : void { |
229
|
37 |
|
foreach ($categories as $setting) { |
230
|
|
|
// Typecast settings |
231
|
11 |
|
$setting = (array) $setting; |
232
|
11 |
|
$category = new Category($setting['name'] ?? '', $setting['id'] ?? null); |
233
|
|
|
|
234
|
11 |
|
if (!isset(self::$root)) { |
235
|
6 |
|
self::$root = $category; |
236
|
|
|
} |
237
|
|
|
|
238
|
11 |
|
self::setSkip($category, $setting); |
239
|
|
|
|
240
|
|
|
// Set parent if exists |
241
|
11 |
|
if (isset(self::$categories[$setting['id'] ?? $category->getId()])) { |
242
|
5 |
|
self::$categories[$setting['id'] ?? $category->getId()]->addCategory($category); |
243
|
|
|
} |
244
|
|
|
|
245
|
11 |
|
self::logAllIds($setting, $category); |
246
|
|
|
} |
247
|
37 |
|
} |
248
|
|
|
|
249
|
|
|
/** |
250
|
|
|
* Set skip setting to an object |
251
|
|
|
* |
252
|
|
|
* @param WithSkipSetters $category |
253
|
|
|
* @param array $setting |
254
|
|
|
*/ |
255
|
28 |
|
protected static function setSkip(WithSkipSetters $category, array $setting) : void { |
256
|
28 |
|
if (isset($setting['skip'])) { |
257
|
25 |
|
$category->setSkip($setting['skip']); |
258
|
|
|
} |
259
|
28 |
|
} |
260
|
|
|
|
261
|
|
|
/** |
262
|
|
|
* Create round objects |
263
|
|
|
* |
264
|
|
|
* @param array $rounds |
265
|
|
|
*/ |
266
|
37 |
|
protected static function createRounds(array $rounds) : void { |
267
|
37 |
|
foreach ($rounds as $setting) { |
268
|
|
|
// Typecast settings |
269
|
17 |
|
$setting = (array) $setting; |
270
|
17 |
|
$round = new Round($setting['name'] ?? '', $setting['id'] ?? null); |
271
|
|
|
|
272
|
17 |
|
if (!isset(self::$root)) { |
273
|
8 |
|
self::$root = $round; |
274
|
|
|
} |
275
|
|
|
|
276
|
17 |
|
self::setSkip($round, $setting); |
277
|
|
|
|
278
|
|
|
// Set parent if exists |
279
|
17 |
|
if (isset(self::$rounds[$setting['id'] ?? $round->getId()])) { |
280
|
9 |
|
self::$rounds[$setting['id'] ?? $round->getId()]->addRound($round); |
281
|
|
|
} |
282
|
17 |
|
self::logAllIds($setting, $round); |
283
|
|
|
} |
284
|
37 |
|
} |
285
|
|
|
|
286
|
|
|
/** |
287
|
|
|
* Create group objects |
288
|
|
|
* |
289
|
|
|
* @param array $groups |
290
|
|
|
* @param array $allGroups |
291
|
|
|
* |
292
|
|
|
* @throws Exception |
293
|
|
|
*/ |
294
|
37 |
|
protected static function createGroups(array $groups, array &$allGroups) : void { |
295
|
37 |
|
foreach ($groups as $setting) { |
296
|
|
|
// Typecast settings |
297
|
17 |
|
$setting = (array) $setting; |
298
|
17 |
|
$group = new Group($setting['name'] ?? '', $setting['id'] ?? null); |
299
|
17 |
|
$allGroups[$group->getId()] = $group; |
300
|
|
|
|
301
|
17 |
|
if (!isset(self::$root)) { |
302
|
6 |
|
self::$root = $group; |
303
|
|
|
} |
304
|
|
|
|
305
|
17 |
|
self::setSkip($group, $setting); |
306
|
17 |
|
self::setGroup($group, $setting); |
307
|
|
|
|
308
|
|
|
// Set parent if exists |
309
|
17 |
|
if (isset(self::$groups[$setting['id'] ?? $group->getId()])) { |
310
|
11 |
|
self::$groups[$setting['id'] ?? $group->getId()]->addGroup($group); |
311
|
|
|
} |
312
|
17 |
|
self::logAllIds($setting, $group); |
313
|
|
|
} |
314
|
37 |
|
} |
315
|
|
|
|
316
|
|
|
/** |
317
|
|
|
* Setup a group with all its settings |
318
|
|
|
* |
319
|
|
|
* @param Group $group |
320
|
|
|
* @param array $setting |
321
|
|
|
* |
322
|
|
|
* @throws Exception |
323
|
|
|
*/ |
324
|
17 |
|
protected static function setGroup(Group $group, array $setting) : void { |
325
|
17 |
|
foreach ($setting as $key => $value) { |
326
|
17 |
|
switch ($key) { |
327
|
17 |
|
case 'type': |
328
|
17 |
|
$group->setType($value); |
329
|
17 |
|
break; |
330
|
17 |
|
case 'points': |
331
|
17 |
|
self::setPoints($group, (array) $value); |
332
|
17 |
|
break; |
333
|
17 |
|
case 'inGame': |
334
|
17 |
|
$group->setInGame($value); |
335
|
17 |
|
break; |
336
|
17 |
|
case 'maxSize': |
337
|
17 |
|
$group->setMaxSize($value); |
338
|
17 |
|
break; |
339
|
|
|
} |
340
|
|
|
} |
341
|
17 |
|
} |
342
|
|
|
|
343
|
|
|
/** |
344
|
|
|
* Set points setting to an object |
345
|
|
|
* |
346
|
|
|
* @param Group $object |
347
|
|
|
* @param array $setting |
348
|
|
|
*/ |
349
|
17 |
|
protected static function setPoints(Group $object, array $setting) : void { |
350
|
17 |
|
foreach ($setting as $key2 => $value2) { |
351
|
17 |
|
switch ($key2) { |
352
|
17 |
|
case 'win': |
353
|
17 |
|
$object->setWinPoints($value2); |
354
|
17 |
|
break; |
355
|
17 |
|
case 'loss': |
356
|
17 |
|
$object->setLostPoints($value2); |
357
|
17 |
|
break; |
358
|
17 |
|
case 'draw': |
359
|
17 |
|
$object->setDrawPoints($value2); |
360
|
17 |
|
break; |
361
|
17 |
|
case 'second': |
362
|
17 |
|
$object->setSecondPoints($value2); |
363
|
17 |
|
break; |
364
|
17 |
|
case 'third': |
365
|
17 |
|
$object->setThirdPoints($value2); |
366
|
17 |
|
break; |
367
|
17 |
|
case 'progression': |
368
|
17 |
|
$object->setProgressPoints($value2); |
369
|
17 |
|
break; |
370
|
|
|
} |
371
|
|
|
} |
372
|
17 |
|
} |
373
|
|
|
|
374
|
|
|
/** |
375
|
|
|
* Create all progressions |
376
|
|
|
* |
377
|
|
|
* @param array $progressions |
378
|
|
|
* @param array $allGroups |
379
|
|
|
*/ |
380
|
37 |
|
protected static function createProgressions(array $progressions, array $allGroups) : void { |
381
|
37 |
|
foreach ($progressions as $setting) { |
382
|
|
|
// Typecast settings |
383
|
4 |
|
$setting = (array) $setting; |
384
|
|
|
|
385
|
4 |
|
if (isset($setting['from'], $setting['to'], $allGroups[$setting['from']], $allGroups[$setting['to']])) { |
386
|
4 |
|
$progression = $allGroups[$setting['from']]->progression($allGroups[$setting['to']], $setting['offset'] ?? 0, $setting['length'] ?? null); |
387
|
|
|
|
388
|
|
|
// Setup filters |
389
|
4 |
|
foreach ($setting['filters'] ?? [] as $filterSetting) { |
390
|
|
|
// Typecast settings |
391
|
2 |
|
$filterSetting = (array) $filterSetting; |
392
|
|
|
|
393
|
2 |
|
self::$groups = array_map(static function($groupId) use ($allGroups) { |
394
|
2 |
|
return $allGroups[$groupId] ?? null; |
395
|
2 |
|
}, $filterSetting['groups'] ?? []); |
396
|
|
|
|
397
|
2 |
|
$filter = new TeamFilter($filterSetting['what'] ?? 'points', $filterSetting['how'] ?? '>', $filterSetting['val'] ?? 0, self::$groups); |
398
|
2 |
|
$progression->addFilter($filter); |
399
|
|
|
} |
400
|
|
|
|
401
|
4 |
|
if (isset($setting['progressed'])) { |
402
|
4 |
|
$progression->setProgressed($setting['progressed']); |
403
|
|
|
} |
404
|
|
|
} |
405
|
|
|
} |
406
|
37 |
|
} |
407
|
|
|
|
408
|
|
|
/** |
409
|
|
|
* Create all team objects |
410
|
|
|
* |
411
|
|
|
* @param array $teams |
412
|
|
|
* @param array $allTeams |
413
|
|
|
*/ |
414
|
37 |
|
protected static function createTeams(array $teams, array &$allTeams) : void { |
415
|
37 |
|
foreach ($teams as $setting) { |
416
|
|
|
// Typecast settings |
417
|
14 |
|
$setting = (array) $setting; |
418
|
14 |
|
$team = new Team($setting['name'] ?? '', $setting['id'] ?? null); |
419
|
14 |
|
$allTeams[$team->getId()] = $team; |
420
|
|
|
|
421
|
14 |
|
if (!isset(self::$root)) { |
422
|
3 |
|
self::$root = $team; |
423
|
|
|
} |
424
|
|
|
|
425
|
|
|
// Set parent if exists |
426
|
14 |
|
if (isset(self::$teams[$setting['id'] ?? $team->getId()])) { |
427
|
11 |
|
foreach (self::$teams[$setting['id'] ?? $team->getId()] as $object) { |
428
|
11 |
|
$object->addTeam($team); |
429
|
|
|
} |
430
|
|
|
} |
431
|
|
|
} |
432
|
37 |
|
} |
433
|
|
|
|
434
|
|
|
/** |
435
|
|
|
* Create all game objects |
436
|
|
|
* |
437
|
|
|
* @param $games |
438
|
|
|
* @param array $allTeams |
439
|
|
|
*/ |
440
|
37 |
|
protected static function createGames($games, array $allTeams) : void { |
441
|
37 |
|
foreach ($games as $setting) { |
442
|
|
|
// Typecast settings |
443
|
9 |
|
$setting = (array) $setting; |
444
|
|
|
|
445
|
9 |
|
$gameTeams = array_map(static function($teamId) use ($allTeams) { |
446
|
9 |
|
return $allTeams[$teamId] ?? null; |
447
|
9 |
|
}, $setting['teams'] ?? []); |
448
|
|
|
|
449
|
|
|
// Check if parent group exists |
450
|
9 |
|
if (isset($setting['id'], self::$games[$setting['id']])) { |
451
|
|
|
|
452
|
9 |
|
$game = self::$games[$setting['id']]->game($gameTeams); |
453
|
|
|
|
454
|
|
|
// Set results |
455
|
9 |
|
if (isset($setting['scores'])) { |
456
|
3 |
|
$scores = array_map(static function($info) { |
457
|
3 |
|
return ((array) $info)['score'] ?? 0; |
458
|
3 |
|
}, $setting['scores']); |
459
|
3 |
|
$game->setResults($scores); |
460
|
|
|
} |
461
|
|
|
|
462
|
|
|
} |
463
|
|
|
} |
464
|
37 |
|
} |
465
|
|
|
|
466
|
|
|
} |
This check looks for function or method calls that always return null and whose return value is used.
The method
getObject()
can return nothing but null, so it makes no sense to use the return value.The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.