Files
myep/src/Service/BookingEditPreFlightChecker.php
T
2026-07-10 14:33:09 +02:00

151 lines
4.6 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Service;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Form\Model\ParticipantEditDto;
use Symfony\Component\Validator\ConstraintViolationInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
/**
* Detects edit-overview participants that still need user attention.
*
* This is display-only: it highlights participants that still need work
* without affecting whether the edit submit is allowed.
*
* The scan uses the participant edit wrapper so participant-scoped validation
* follows the same booking-context rules as the participant form. Body
* dimensions are optional; only provided values are checked against the range
* validator unless a selection-based exemption applies.
*/
class BookingEditPreFlightChecker
{
public function __construct(
private readonly ValidatorInterface $validator,
) {
}
/**
* Returns the display-only attention scan for the edit overview.
*
* Canceled participants are ignored because they are not actionable.
*
* @return array<int, list<string>>
*/
public function findMissingValueLabelsByParticipantIndex(BookingDto $bookingDto): array
{
if (BookingDto::MODE_EDIT !== $bookingDto->getMode()) {
return [];
}
$missingValueLabelsByParticipantIndex = [];
foreach ($bookingDto->participants as $index => $participant) {
if (true === $participant->isCanceled()) {
continue;
}
$missingValueLabels = $this->getMissingValueLabelsForParticipant($bookingDto, $participant);
if ([] !== $missingValueLabels) {
$missingValueLabelsByParticipantIndex[$index] = $missingValueLabels;
}
}
return $missingValueLabelsByParticipantIndex;
}
/**
* @return list<string>
*/
private function getMissingValueLabelsForParticipant(BookingDto $bookingDto, ParticipantDto $participant): array
{
$participantForAudit = new ParticipantEditDto(
participant: $participant,
bookingContext: $bookingDto,
);
$violations = $this->validator->validate(
$participantForAudit,
null,
['booking_edit', 'strict_required']
);
$valueLabels = [];
foreach ($violations as $violation) {
$valueLabel = $this->mapViolationToValueLabel($violation);
if (null !== $valueLabel) {
$valueLabels[] = $valueLabel;
}
}
if (false === $bookingDto->isInternalAgencyBooking() && true === $participant->isApplicant()) {
foreach ($this->getMissingValueAddressLabels($participant) as $valueLabel) {
$valueLabels[] = $valueLabel;
}
}
return array_values(array_unique($valueLabels));
}
private function mapViolationToValueLabel(ConstraintViolationInterface $violation): ?string
{
$propertyPath = $violation->getPropertyPath();
if (str_starts_with($propertyPath, 'participant.')) {
$propertyPath = substr($propertyPath, strlen('participant.'));
}
return match ($propertyPath) {
'firstName' => 'Vorname',
'lastName' => 'Nachname',
'gender' => 'Anrede',
'nationality' => 'Nationalität',
'dateOfBirth' => 'Geburtsdatum',
'email' => 'E-Mail',
'height' => 'Körpergröße',
'weight' => 'Gewicht',
'shoeSize' => 'Schuhgröße',
'assignedRoomId' => 'Zimmer',
'transportationOutbound' => 'Anreise',
'pickup' => 'Zustieg',
'transportationInbound' => 'Abreise',
'dropOff' => 'Ausstieg',
default => null,
};
}
/**
* @return list<string>
*/
private function getMissingValueAddressLabels(ParticipantDto $participant): array
{
if (null === $participant->address) {
return ['Adresse'];
}
$valueLabels = [];
$violations = $this->validator->validate($participant->address, null, ['personal_data']);
foreach ($violations as $violation) {
$valueLabel = match ($violation->getPropertyPath()) {
'street' => 'Straße',
'postCode' => 'PLZ',
'city' => 'Ort',
'country' => 'Land',
default => null,
};
if (null !== $valueLabel) {
$valueLabels[] = $valueLabel;
}
}
return array_values(array_unique($valueLabels));
}
}