feat: exclusion groups for additional services
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Service;
|
||||
|
||||
use App\Entity\Groups\AdditionalService;
|
||||
|
||||
/**
|
||||
* Resolves mutually exclusive additional services.
|
||||
*
|
||||
* Services sharing an exclusion group interact as soon as at least one of the two involved
|
||||
* services is flagged as exclusive: selecting the exclusive one clears all other members of
|
||||
* the group, selecting any other member clears the exclusive ones. Members that are not
|
||||
* exclusive can be combined freely.
|
||||
*
|
||||
* Conflicts are resolved in favour of the newest selection, determined by diffing the
|
||||
* submitted ids against the previously stored ones.
|
||||
*/
|
||||
class AdditionalServiceExclusionResolver
|
||||
{
|
||||
/**
|
||||
* @param AdditionalService[] $availableServices
|
||||
* @param array<array-key, int> $submittedIds keys are preserved, grouped radios submit under their group name
|
||||
* @param array<array-key, int> $previousIds
|
||||
*
|
||||
* @return array<array-key, int>
|
||||
*/
|
||||
public function resolve(array $availableServices, array $submittedIds, array $previousIds): array
|
||||
{
|
||||
$rules = $this->buildRules($availableServices);
|
||||
|
||||
if ([] === $rules) {
|
||||
return $submittedIds;
|
||||
}
|
||||
|
||||
$added = array_values(array_diff($submittedIds, $previousIds));
|
||||
|
||||
foreach ($added as $addedId) {
|
||||
if (!isset($rules[$addedId])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$submittedIds = array_filter(
|
||||
$submittedIds,
|
||||
fn(int $id) => !$this->conflicts($rules, $addedId, $id),
|
||||
);
|
||||
}
|
||||
|
||||
return $submittedIds;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<int, array{group: string, exclusive: bool}> $rules
|
||||
*/
|
||||
private function conflicts(array $rules, int $a, int $b): bool
|
||||
{
|
||||
if ($a === $b || !isset($rules[$b])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($rules[$a]['group'] !== $rules[$b]['group']) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $rules[$a]['exclusive'] || $rules[$b]['exclusive'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param AdditionalService[] $availableServices
|
||||
*
|
||||
* @return array<int, array{group: string, exclusive: bool}>
|
||||
*/
|
||||
private function buildRules(array $availableServices): array
|
||||
{
|
||||
$rules = [];
|
||||
|
||||
foreach ($availableServices as $service) {
|
||||
$group = $service->getExclusionGroup();
|
||||
$id = $service->getId();
|
||||
|
||||
if (null === $id || null === $group || '' === $group) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$rules[$id] = ['group' => $group, 'exclusive' => $service->isExclusive()];
|
||||
}
|
||||
|
||||
return $rules;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user