92 lines
2.6 KiB
PHP
92 lines
2.6 KiB
PHP
<?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;
|
|
}
|
|
}
|