Lines
100.00%
46 / 46
Functions and Methods
100.00%
8 / 8
Classes and Traits
100.00%
1 / 1
| Name | Lines | Functions and Methods | CRAP | Classes and Traits | ||||||
|---|---|---|---|---|---|---|---|---|---|---|
| CustomMedalsJsonImporter | 100.00% | 46 / 46 | 100.00% | 8 / 8 | 30 | 100.00% | 1 / 1 | |||
| __construct | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | |||||
| import | 100.00% | 11 / 11 | 100.00% | 1 / 1 | 3 | |||||
| known | 100.00% | 4 / 4 | 100.00% | 1 / 1 | 2 | |||||
| importMedal | 100.00% | 5 / 5 | 100.00% | 1 / 1 | 5 | |||||
| decode | 100.00% | 8 / 8 | 100.00% | 1 / 1 | 3 | |||||
| entries | 100.00% | 3 / 3 | 100.00% | 1 / 1 | 4 | |||||
| rank | 100.00% | 6 / 6 | 100.00% | 1 / 1 | 8 | |||||
| merge | 100.00% | 8 / 8 | 100.00% | 1 / 1 | 4 | |||||
| 1 | <?php | |
| 2 | ||
| 3 | declare(strict_types=1); | |
| 4 | ||
| 5 | namespace App\Import; | |
| 6 | ||
| 7 | use App\Entity\User; | |
| 8 | use App\Medal\CustomMedal; | |
| 9 | use App\Medal\MedalChecker; | |
| 10 | use Doctrine\ORM\EntityManagerInterface; | |
| 11 | ||
| 12 | /** | |
| 13 | * Imports custom medals from a JSON file as 4e-website exports them (Migrate → Export custom | |
| 14 | * medals): {"anomaly": {"orion": "platinum"}, "annual": {"voyager": 5}, "event": {…}}. They are | |
| 15 | * merged with the user's: a medal the user has keeps the higher tier, as the app merges them. | |
| 16 | * Medals or tiers AgentTools does not know are reported. | |
| 17 | */ | |
| 18 | final readonly class CustomMedalsJsonImporter | |
| 19 | { | |
| 20 | public function __construct( | |
| 21 | private MedalChecker $medalChecker, | |
| 22 | private EntityManagerInterface $entityManager, | |
| 23 | ) { | |
| 24 | } | |
| 25 | ||
| 26 | public function import(User $owner, string $json): CustomMedalsImportResult | |
| 27 | { | |
| 28 | $result = new CustomMedalsImportResult(); | |
| 29 | $file = $this->decode($json, $result); | |
| 30 | if (null === $file) { | |
| 31 | return $result; | |
| 32 | } | |
| 33 | ||
| 34 | $known = $this->known(); | |
| 35 | $medals = $owner->getCustomMedals(); | |
| 36 | foreach ($this->entries($file) as [$group, $key, $value]) { | |
| 37 | $this->importMedal($known[$group.'/'.$key] ?? null, $group, $key, $value, $medals, $result); | |
| 38 | } | |
| 39 | ||
| 40 | $owner->setCustomMedals($medals); | |
| 41 | $this->entityManager->flush(); | |
| 42 | ||
| 43 | return $result; | |
| 44 | } | |
| 45 | ||
| 46 | /** | |
| 47 | * @return array<string, CustomMedal> by "group/key" | |
| 48 | */ | |
| 49 | private function known(): array | |
| 50 | { | |
| 51 | $known = []; | |
| 52 | foreach ($this->medalChecker->customMedals() as $medal) { | |
| 53 | $known[$medal->group.'/'.$medal->key] = $medal; | |
| 54 | } | |
| 55 | ||
| 56 | return $known; | |
| 57 | } | |
| 58 | ||
| 59 | /** | |
| 60 | * Merges one medal of the file ([$medal] null for one AgentTools does not know). | |
| 61 | * | |
| 62 | * @param array<string, array<string, int|string>> $medals the user's, changed | |
| 63 | */ | |
| 64 | private function importMedal(?CustomMedal $medal, string $group, string $key, mixed $value, array &$medals, CustomMedalsImportResult $result): void | |
| 65 | { | |
| 66 | $rank = $medal instanceof CustomMedal ? $this->rank($medal, $value) : null; | |
| 67 | if (!$medal instanceof CustomMedal || null === $rank) { | |
| 68 | $result->errors[] = \sprintf('%s: %s %s', $group, $key, \is_scalar($value) ? (string) $value : ''); | |
| 69 | ||
| 70 | return; | |
| 71 | } | |
| 72 | ||
| 73 | $this->merge($medals, $medal, $value, $rank, $result); | |
| 74 | } | |
| 75 | ||
| 76 | /** | |
| 77 | * The file's groups, null (and why in [$result]) for no JSON object. | |
| 78 | * | |
| 79 | * @return array<mixed>|null | |
| 80 | */ | |
| 81 | private function decode(string $json, CustomMedalsImportResult $result): ?array | |
| 82 | { | |
| 83 | try { | |
| 84 | $file = \json_decode($json, true, 8, \JSON_THROW_ON_ERROR); | |
| 85 | } catch (\JsonException $jsonException) { | |
| 86 | $result->errors[] = 'Not JSON: '.$jsonException->getMessage(); | |
| 87 | ||
| 88 | return null; | |
| 89 | } | |
| 90 | ||
| 91 | if (!\is_array($file)) { | |
| 92 | $result->errors[] = 'Not custom medals: an object of groups is expected.'; | |
| 93 | ||
| 94 | return null; | |
| 95 | } | |
| 96 | ||
| 97 | return $file; | |
| 98 | } | |
| 99 | ||
| 100 | /** | |
| 101 | * Every medal of every group in the file. | |
| 102 | * | |
| 103 | * @param array<mixed> $file | |
| 104 | * | |
| 105 | * @return \Generator<array{string, string, mixed}> group, medal key, tier or level | |
| 106 | */ | |
| 107 | private function entries(array $file): \Generator | |
| 108 | { | |
| 109 | foreach ($file as $group => $values) { | |
| 110 | foreach (\is_array($values) ? $values : [] as $key => $value) { | |
| 111 | yield [(string) $group, (string) $key, $value]; | |
| 112 | } | |
| 113 | } | |
| 114 | } | |
| 115 | ||
| 116 | /** | |
| 117 | * How high [$value] is for [$medal] (a tier's position, an annual medal's level), null if it | |
| 118 | * is none of its tiers. | |
| 119 | */ | |
| 120 | private function rank(CustomMedal $medal, mixed $value): ?int | |
| 121 | { | |
| 122 | if ('annual' === $medal->group) { | |
| 123 | return \is_int($value) && $value >= 1 && $value <= 5 ? $value : null; | |
| 124 | } | |
| 125 | ||
| 126 | if ([] === $medal->tiers) { | |
| 127 | return '' === $value ? 0 : null; | |
| 128 | } | |
| 129 | ||
| 130 | $index = \array_search($value, $medal->tiers, true); | |
| 131 | ||
| 132 | return false === $index ? null : $index; | |
| 133 | } | |
| 134 | ||
| 135 | /** | |
| 136 | * @param array<string, array<string, int|string>> $medals the user's, changed | |
| 137 | */ | |
| 138 | private function merge(array &$medals, CustomMedal $medal, mixed $value, int $rank, CustomMedalsImportResult $result): void | |
| 139 | { | |
| 140 | $saved = $medals[$medal->group][$medal->key] ?? null; | |
| 141 | if (null === $saved) { | |
| 142 | ++$result->added; | |
| 143 | } elseif ($rank > ($this->rank($medal, $saved) ?? -1)) { | |
| 144 | ++$result->upgraded; | |
| 145 | } else { | |
| 146 | ++$result->kept; | |
| 147 | ||
| 148 | return; | |
| 149 | } | |
| 150 | ||
| 151 | $medals[$medal->group][$medal->key] = \is_int($value) ? $value : (string) $value; | |
| 152 | } | |
| 153 | } |