Lines
100.00%
21 / 21
Methods
100.00%
6 / 6
Classes
100.00%
1 / 1
| Name | Lines | Methods | CRAP | ||||
|---|---|---|---|---|---|---|---|
| __construct | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| fromArray | 100.00% | 4 / 4 | 100.00% | 1 / 1 | 1 | ||
| toArray | 100.00% | 4 / 4 | 100.00% | 1 / 1 | 1 | ||
| validate | 100.00% | 5 / 5 | 100.00% | 1 / 1 | 4 | ||
| error | 100.00% | 6 / 6 | 100.00% | 1 / 1 | 9 | ||
| isKey | 100.00% | 1 / 1 | 100.00% | 1 / 1 | 1 | ||
| 17 | final class CustomMedals | |
| 18 | { | |
| 19 | /** Medal and tier keys of 4e-website, e.g. plus_gamma_onsite, 2026_summer_solstice, platinum. */ | |
| 20 | private const string KEY_PATTERN = '/^[a-z0-9_]{1,50}$/'; | |
| 21 | ||
| 22 | private const array GROUPS = ['anomaly', 'annual', 'event']; | |
| 23 | ||
| 24 | /** | |
| 25 | * @param array<string, string> $anomaly medal => tier ("" for a medal without tiers) | |
| 26 | * @param array<string, int> $annual medal => level 1 (bronze) to 5 (onyx) | |
| 27 | * @param array<string, string> $event medal => tier ("" for a medal without tiers) | |
| 28 | */ | |
| 29 | public function __construct( | |
| 30 | #[Groups([Me::READ, Me::WRITE, Agent::ITEM])] | |
| 31 | public array $anomaly = [], | |
| 32 | #[Groups([Me::READ, Me::WRITE, Agent::ITEM])] | |
| 33 | public array $annual = [], | |
| 34 | #[Groups([Me::READ, Me::WRITE, Agent::ITEM])] | |
| 35 | public array $event = [], | |
| 36 | ) { | |
| 37 | } | |
| 38 | ||
| 39 | /** | |
| 40 | * @param array<string, array<string, int|string>> $medals as stored on the User | |
| 41 | */ | |
| 42 | public static function fromArray(array $medals): self | |
| 43 | { | |
| 44 | /** @var array<string, string> $anomaly */ | |
| 45 | $anomaly = $medals['anomaly'] ?? []; | |
| 46 | /** @var array<string, int> $annual */ | |
| 47 | $annual = $medals['annual'] ?? []; | |
| 48 | /** @var array<string, string> $event */ | |
| 49 | $event = $medals['event'] ?? []; | |
| 50 | ||
| 51 | return new self($anomaly, $annual, $event); | |
| 52 | } | |
| 53 | ||
| 54 | /** | |
| 55 | * @return array<string, array<string, int|string>> for the User, empty groups left out | |
| 56 | */ | |
| 57 | public function toArray(): array | |
| 58 | { | |
| 59 | return \array_filter( | |
| 60 | ['anomaly' => $this->anomaly, 'annual' => $this->annual, 'event' => $this->event], | |
| 61 | static fn (array $medals): bool => [] !== $medals, | |
| 62 | ); | |
| 63 | } | |
| 64 | ||
| 65 | #[Assert\Callback] | |
| 66 | public function validate(ExecutionContextInterface $context): void | |
| 67 | { | |
| 68 | foreach (self::GROUPS as $group) { | |
| 69 | foreach ($this->{$group} as $medal => $value) { | |
| 70 | $error = $this->error($group, (string) $medal, $value); | |
| 71 | if (null !== $error) { | |
| 72 | $context->buildViolation($error[1])->atPath($error[0])->addViolation(); | |
| 73 | } | |
| 74 | } | |
| 75 | } | |
| 76 | } | |
| 77 | ||
| 78 | /** | |
| 79 | * @return array{string, string}|null the property path and the message, null if valid | |
| 80 | */ | |
| 81 | private function error(string $group, string $medal, mixed $value): ?array | |
| 82 | { | |
| 83 | if (!$this->isKey($medal)) { | |
| 84 | return [$group, 'Not a medal key.']; | |
| 85 | } | |
| 86 | ||
| 87 | $path = $group.'['.$medal.']'; | |
| 88 | ||
| 89 | if ('annual' === $group) { | |
| 90 | return \is_int($value) && $value >= 1 && $value <= 5 ? null : [$path, 'The level must be 1 to 5.']; | |
| 91 | } | |
| 92 | ||
| 93 | return \is_string($value) && ('' === $value || $this->isKey($value)) ? null : [$path, 'Not a tier.']; | |
| 94 | } | |
| 95 | ||
| 96 | private function isKey(string $key): bool | |
| 97 | { | |
| 98 | return 1 === \preg_match(self::KEY_PATTERN, $key); | |
| 99 | } | |
| 100 | } |