diff --git a/src/BusProNet/ApiClient.php b/src/BusProNet/ApiClient.php index ca89fc8..6604d04 100644 --- a/src/BusProNet/ApiClient.php +++ b/src/BusProNet/ApiClient.php @@ -7,6 +7,7 @@ use App\BusProNet\Exception\ApiClientException; use App\BusProNet\Exception\ImmediateConnectionCloseException; use App\BusProNet\Exception\ResponseParserException; use App\BusProNet\Exception\TimeoutException; +use App\BusProNet\Model\Agency; use App\BusProNet\Model\BaseData; use App\BusProNet\Model\Booking; use App\BusProNet\Model\BookingResponse; @@ -49,11 +50,13 @@ class ApiClient public const TYPE_PURCHASE_VOUCHER = 'GUTSCHEINPRUEFUNGEINLOESUNG'; public const TYPE_PROMO_VOUCHER = 'AKTIONSGUTSCHEIN'; + /** @var array */ private array $config; private float $operationStartTime; private int $selectedPort; private int $requestCounter = 0; + /** @param array $options */ public function __construct( private readonly SerializerInterface $serializer, private readonly ApiResponseParser $responseParser, @@ -613,6 +616,10 @@ class ApiClient /** * @throws ApiClientException */ + /** + * @param array $data + * @param array $additionalArgs + */ private function sendRequest(string $type, array $data, array $additionalArgs = [], bool $debug = false): mixed { return $this->executeWithRetry(fn () => $this->doSendRequest($type, $data, $additionalArgs, $debug), $type); @@ -621,6 +628,7 @@ class ApiClient /** * @throws ApiClientException */ + /** @param array $data */ private function sendRequestRaw(array $data): string { return $this->executeWithRetry(fn () => $this->doSendRequestRaw($data)); @@ -676,6 +684,10 @@ class ApiClient * @throws ApiClientException * @throws ImmediateConnectionCloseException */ + /** + * @param array $data + * @param array $additionalArgs + */ private function doSendRequest(string $type, array $data, array $additionalArgs = [], bool $debug = false): mixed { $requestId = $this->getRequestId(); @@ -733,6 +745,8 @@ class ApiClient } /** + * @param array $data + * * @throws ApiClientException * @throws ImmediateConnectionCloseException */ @@ -794,6 +808,8 @@ class ApiClient } /** + * @return resource + * * @throws ApiClientException * @throws TimeoutException */ @@ -857,6 +873,8 @@ class ApiClient } /** + * @param resource $socket + * * @throws TimeoutException */ private function send($socket, string $data): void @@ -875,6 +893,8 @@ class ApiClient } /** + * @param resource $socket + * * @throws TimeoutException * @throws ImmediateConnectionCloseException */ @@ -937,11 +957,17 @@ class ApiClient return $response; } + /** @param resource $socket */ private function disconnect($socket): void { @fclose($socket); } + /** + * @param array $options + * + * @return array + */ private function resolveOptions(array $options): array { $optionsResolver = new OptionsResolver(); diff --git a/src/BusProNet/DataProcessor/BookingDataProcessor.php b/src/BusProNet/DataProcessor/BookingDataProcessor.php index 0351c3c..30f789d 100644 --- a/src/BusProNet/DataProcessor/BookingDataProcessor.php +++ b/src/BusProNet/DataProcessor/BookingDataProcessor.php @@ -73,14 +73,12 @@ class BookingDataProcessor if (0 === $index) { // Copy address if first participant has no street (indicating empty/incomplete address) // This allows applicant and first participant to be different people with different addresses - if (null !== $booking->applicant->address) { - $isEmpty = null === $participantData->address - || null === $participantData->address->street - || '' === trim($participantData->address->street); + $isEmpty = null === $participantData->address + || null === $participantData->address->street + || '' === trim($participantData->address->street); - if ($isEmpty) { - $participantData->address = clone $booking->applicant->address; - } + if ($isEmpty) { + $participantData->address = clone $booking->applicant->address; } // Copy body dimensions from applicant if not present in participant @@ -284,7 +282,7 @@ class BookingDataProcessor * * @param BookingDto|null $formData The booking edit form data containing updated participant and service selections * - * @return array The structured payload array ready for BusProNet API submission + * @return array The structured payload array ready for BusProNet API submission */ public function createUpdateRequestPayload(?BookingDto $formData): array { @@ -345,7 +343,7 @@ class BookingDataProcessor * @param BookingDto $bookingDto The booking creation form data * @param string $bookingType Either 'Anfrage' (inquiry) or 'Buchung' (final booking) * - * @return array The structured payload array for BusProNet API submission + * @return array The structured payload array for BusProNet API submission */ public function createBookingRequestPayload(BookingDto $bookingDto, string $bookingType): array { diff --git a/src/BusProNet/DataProcessor/BookingPayloadBuilder.php b/src/BusProNet/DataProcessor/BookingPayloadBuilder.php index 3a61c30..6940d86 100644 --- a/src/BusProNet/DataProcessor/BookingPayloadBuilder.php +++ b/src/BusProNet/DataProcessor/BookingPayloadBuilder.php @@ -7,6 +7,7 @@ namespace App\BusProNet\DataProcessor; use App\BusProNet\Constants; use App\BusProNet\Model\Booking; use App\Form\Model\BookingDto; +use App\Form\Model\ParticipantDto; /** * Builds API payload structures for BusProNet booking requests. @@ -30,7 +31,7 @@ class BookingPayloadBuilder * * @param Booking $bookingData The booking data object * - * @return array The base payload structure + * @return array The base payload structure */ public function buildBasePayload(Booking $bookingData): array { @@ -66,8 +67,8 @@ class BookingPayloadBuilder * * Bank account information is required for direct debit payments. * - * @param array $payload The payload array to modify - * @param Booking $bookingData The booking data object + * @param array $payload The payload array to modify + * @param Booking $bookingData The booking data object */ public function addBankAccountToPayload(array &$payload, Booking $bookingData): void { @@ -86,9 +87,9 @@ class BookingPayloadBuilder * * Includes status, personal data, and wishes (room remarks, license plate) for each participant. * - * @param array $payload The payload array to modify - * @param Booking $bookingData The booking data object - * @param array $participantDtos The participant DTOs from the form (for wishes data) + * @param array $payload The payload array to modify + * @param Booking $bookingData The booking data object + * @param array $participantDtos The participant DTOs from the form (for wishes data) */ public function buildParticipantPayload(array &$payload, Booking $bookingData, array $participantDtos): void { @@ -141,8 +142,8 @@ class BookingPayloadBuilder * Includes additional services, transportation services, and accommodation details * with participant mappings and quantities. * - * @param array $payload The payload array to modify - * @param Booking $bookingData The booking data object + * @param array $payload The payload array to modify + * @param Booking $bookingData The booking data object */ public function buildServicesPayload(array &$payload, Booking $bookingData): void { @@ -198,8 +199,8 @@ class BookingPayloadBuilder * * Only included in payload if there are actual pickup assignments for bus transportation. * - * @param array $payload The payload array to modify - * @param Booking $bookingData The booking data object + * @param array $payload The payload array to modify + * @param Booking $bookingData The booking data object */ public function buildPickupPayload(array &$payload, Booking $bookingData): void { @@ -223,8 +224,8 @@ class BookingPayloadBuilder * * Only included in payload if there are actual drop-off assignments for bus transportation. * - * @param array $payload The payload array to modify - * @param Booking $bookingData The booking data object + * @param array $payload The payload array to modify + * @param Booking $bookingData The booking data object */ public function buildDropOffPayload(array &$payload, Booking $bookingData): void { @@ -249,7 +250,7 @@ class BookingPayloadBuilder * @param BookingDto $bookingDto The booking creation form data * @param string $bookingType Either 'Anfrage' (inquiry) or 'Buchung' (final booking) * - * @return array The structured payload array for BusProNet API submission + * @return array The structured payload array for BusProNet API submission */ public function buildCreatePayload(BookingDto $bookingDto, string $bookingType): array { @@ -439,11 +440,11 @@ class BookingPayloadBuilder * Generic helper method that converts service ID => participant IDs mappings * into XML payload structure. * - * @param array $payload The payload array to modify - * @param string $sectionKey The section key (e.g., 'beförderungen', 'versicherungen') - * @param string $itemKey The item key (e.g., 'beförderung', 'versicherung') - * @param string $idAttributeName The ID attribute name (e.g., '@idleistung', '@idversicherung') - * @param array $serviceMap Map of service ID to participant IDs + * @param array $payload The payload array to modify + * @param string $sectionKey The section key (e.g., 'beförderungen', 'versicherungen') + * @param string $itemKey The item key (e.g., 'beförderung', 'versicherung') + * @param string $idAttributeName The ID attribute name (e.g., '@idleistung', '@idversicherung') + * @param array $serviceMap Map of service ID to participant IDs */ public function addServicesFromMap( array &$payload, @@ -475,9 +476,9 @@ class BookingPayloadBuilder * - abreise (departure date) * - anzahl (number of rooms of this type booked) * - * @param array $payload The payload array to modify - * @param array $roomMap Map of room ID to participant IDs - * @param BookingDto $bookingDto The booking data for accessing room details and quantities + * @param array $payload The payload array to modify + * @param array $roomMap Map of room ID to participant IDs + * @param BookingDto $bookingDto The booking data for accessing room details and quantities */ public function addRoomMappingsToPayload(array &$payload, array $roomMap, BookingDto $bookingDto): void { @@ -520,8 +521,8 @@ class BookingPayloadBuilder /** * Adds purchase vouchers to the payload. * - * @param array $payload The payload array to modify - * @param BookingDto $bookingDto The booking data + * @param array $payload The payload array to modify + * @param BookingDto $bookingDto The booking data */ public function addPurchaseVouchersToPayload(array &$payload, BookingDto $bookingDto): void { diff --git a/src/BusProNet/DataProcessor/PersonalDataSynchronizer.php b/src/BusProNet/DataProcessor/PersonalDataSynchronizer.php index 33c9d10..f2c130c 100644 --- a/src/BusProNet/DataProcessor/PersonalDataSynchronizer.php +++ b/src/BusProNet/DataProcessor/PersonalDataSynchronizer.php @@ -6,7 +6,6 @@ namespace App\BusProNet\DataProcessor; use App\BusProNet\Model\Address; use App\BusProNet\Model\Booking; -use App\BusProNet\Model\Communication; use App\BusProNet\Model\PersonalData; use App\Form\Model\ParticipantDto; @@ -86,9 +85,6 @@ class PersonalDataSynchronizer } if ($participant->email || $participant->mobile) { - if (null === $bookingData->participants[$participant->index]->communication) { - $bookingData->participants[$participant->index]->communication = new Communication(); - } $bookingData->participants[$participant->index]->communication->email = $participant->email; $bookingData->participants[$participant->index]->communication->mobile = $participant->mobile; } @@ -127,10 +123,6 @@ class PersonalDataSynchronizer $applicant->dateOfBirth = new \DateTimeImmutable('-20 years'); } - if (null === $applicant->communication) { - $applicant->communication = new Communication(); - } - if (null === $applicant->communication->mobile || '' === $applicant->communication->mobile) { $applicant->communication->mobile = '12345'; } diff --git a/src/BusProNet/DataProcessor/ServiceMappingCollector.php b/src/BusProNet/DataProcessor/ServiceMappingCollector.php index 653688f..71a3deb 100644 --- a/src/BusProNet/DataProcessor/ServiceMappingCollector.php +++ b/src/BusProNet/DataProcessor/ServiceMappingCollector.php @@ -20,7 +20,7 @@ class ServiceMappingCollector * * Groups participants by their assigned room ID. * - * @return array> Map of room ID to participant IDs + * @return array> Map of room ID to participant IDs */ public function collectRoomMappings(BookingDto $bookingDto): array { diff --git a/src/BusProNet/DataProvider/CountryDataProvider.php b/src/BusProNet/DataProvider/CountryDataProvider.php index e509d87..df7611e 100644 --- a/src/BusProNet/DataProvider/CountryDataProvider.php +++ b/src/BusProNet/DataProvider/CountryDataProvider.php @@ -14,6 +14,7 @@ class CountryDataProvider { } + /** @return array */ public function getAll(): array { try { diff --git a/src/BusProNet/Form/CountryType.php b/src/BusProNet/Form/CountryType.php index ffbfdc0..ae43109 100644 --- a/src/BusProNet/Form/CountryType.php +++ b/src/BusProNet/Form/CountryType.php @@ -10,6 +10,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\OptionsResolver\Options; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class CountryType extends AbstractType { public function __construct(private readonly CountryDataProvider $countries) diff --git a/src/BusProNet/Model/AgeConstraintResult.php b/src/BusProNet/Model/AgeConstraintResult.php index e508837..af375e3 100644 --- a/src/BusProNet/Model/AgeConstraintResult.php +++ b/src/BusProNet/Model/AgeConstraintResult.php @@ -12,6 +12,7 @@ namespace App\BusProNet\Model; */ class AgeConstraintResult { + /** @param array $metadata */ public function __construct( public readonly string $type, public readonly ?int $ageFrom = null, diff --git a/src/BusProNet/Model/BaseData.php b/src/BusProNet/Model/BaseData.php index 9b43693..b450b62 100644 --- a/src/BusProNet/Model/BaseData.php +++ b/src/BusProNet/Model/BaseData.php @@ -12,6 +12,7 @@ namespace App\BusProNet\Model; */ class BaseData { + /** @param array $items */ public function __construct(private readonly array $items) { } @@ -19,7 +20,7 @@ class BaseData /** * Retrieves all items in the base data collection. * - * @return array The complete items array + * @return array The complete items array */ public function getItems(): array { diff --git a/src/BusProNet/Model/Booking.php b/src/BusProNet/Model/Booking.php index 18f831c..732042b 100644 --- a/src/BusProNet/Model/Booking.php +++ b/src/BusProNet/Model/Booking.php @@ -220,7 +220,7 @@ class Booking public function getInsuranceForParticipant(int $participantIndex): ?Insurance { foreach ($this->insurances as $insurance) { - if (in_array($participantIndex, $insurance->mapping ?? [], true)) { + if (in_array($participantIndex, $insurance->mapping, true)) { return $insurance; } } diff --git a/src/BusProNet/Model/CrmSelectionGroup.php b/src/BusProNet/Model/CrmSelectionGroup.php index 7627296..0bd83a7 100644 --- a/src/BusProNet/Model/CrmSelectionGroup.php +++ b/src/BusProNet/Model/CrmSelectionGroup.php @@ -19,7 +19,7 @@ class CrmSelectionGroup * Maps BusPro IDs of CRM selection groups representing * included services. * - * @var array + * @var array */ public static array $includedServicesMapping = [ 7 => 'Skipass', diff --git a/src/BusProNet/Model/Error.php b/src/BusProNet/Model/Error.php deleted file mode 100644 index bde02f0..0000000 --- a/src/BusProNet/Model/Error.php +++ /dev/null @@ -1,115 +0,0 @@ - - */ - private static array $errors = [ - 100 => 'Anfrageknoten fehlt', - 101 => 'Satzknoten fehlt in Anfrageknoten', - 102 => 'Ungültiger Satztyp [#1#]', - 103 => 'User fehlt', - 104 => 'User fehlerhaft', - 105 => 'Key fehlt', - 106 => 'Key fehlerhaft', - 200 => 'Keine Einträge vorhanden', - 201 => 'Keine Partner zur Auswahl gefunden', - 202 => 'Keinen Partner mit der ID [#1#] gefunden', - 203 => 'Ungültiger Termin', - 300 => 'IDReise fehlt', - 301 => 'Reise nicht gefunden', - 302 => 'IDLeistung (Hin & Rück) fehlt', - 303 => 'IDLeistung passt nicht zu IDReise', - 304 => 'Es konnte nicht für alle Teilnehmer ein Sitzplatz ermittelt werden', - 305 => 'Anzahl Personen fehlt', - 306 => 'Leistungen mit ID #1# nicht gefunden', - 307 => 'Leistungen mit ID #1# passen nicht zur Reise mit ID #2#', - 308 => 'Keine Zahlungsarten gefunden', - 400 => 'IDPartner fehlt', - 401 => 'Partner nicht gefunden', - 402 => 'Bis-Termin fehlt', - 403 => 'Keine Unterbringungsleistungen gefunden', - 500 => 'Art fehlt', - 501 => 'Reise- und Buchungszeitraum fehlen', - 502 => 'Keine Kunden gefunden', - 600 => 'Keine Gutscheine gefunden', - 601 => 'Gutschein-IDs fehlen', - 602 => 'Gutscheine mit ID #1# nicht gefunden', - 610 => 'Buchungsart fehlt', - 611 => 'Buchungsart falsch', - 612 => 'Gutscheinart fehlt', - 613 => 'Gutscheinart falsch', - 614 => 'Gutscheinstamm-ID fehlt', - 615 => 'Gutschein (Stamm) mit ID #1# nicht gefunden', - 616 => 'Kapazität beim Gutschein #1# nicht ausreichend', - 617 => 'Rechnungsempfänger fehlt', - 618 => 'Zahlungsart fehlt', - 619 => 'Agentur-ID fehlt', - 620 => 'Agentur mit ID #1# nicht gefunden', - 650 => '#1#', - 700 => 'Keine Agenturen gefunden', - 701 => 'Agentur mit ID #1# nicht gefunden', - 800 => 'Produkt-ID fehlt', - 801 => 'Produkt mit ID #1# nicht gefunden', - 805 => 'Es wurden keine Produkte gefunden', - 810 => 'Stammdaten (#1#) nicht gefunden', - 900 => 'Buchungsart fehlt', - 901 => 'Buchungsart falsch', - 902 => 'Status fehlt', - 903 => 'Status falsch', - 904 => 'Agentur-ID fehlt', - 905 => 'Agentur mit ID #1# nicht gefunden', - 906 => 'Reise-ID fehlt', - 907 => 'Reise mit ID #1# nicht gefunden', - 908 => 'Reise mit ID #1# ist storniert', - 909 => 'Beförderungen fehlen', - 910 => 'Leistung mit ID #1# gehört nicht zur Reise', - 911 => 'Leistung mit ID #1# gehört nicht zum Produkt', - 912 => 'Beförderungsleistung für die #1# fehlt', - 913 => 'Unterbringungen fehlen', - 914 => 'Partner-ID fehlt', - 915 => 'Partner mit ID #1# nicht gefunden', - 916 => 'Ferienziel-Unterbringungen fehlen', - 917 => 'Ferienziel: Zimmer-IDZ fehlt', - 918 => 'Ferienziel: Kategorie fehlt', - 919 => 'Ferienziel: Verpflegungs-ID fehlt', - 920 => 'Ferienziel: Anreise fehlt', - 921 => 'Ferienziel: Anreise passt nicht zur Beförderungsleistung (Hinfahrt)', - 922 => 'Ferienziel: Abreise fehlt', - 923 => 'Ferienziel: Abreise passt nicht zur Beförderungsleistung (Rückfahrt)', - 924 => 'Ferienziel: Keine Preise zu den Daten gefunden', - 925 => 'Ferienziel: Preis zu den Daten nicht gefunden', - 930 => 'Zustiege fehlen', - 931 => 'Zustieg mit ID #1# nicht gefunden', - 932 => 'Zustieg mit ID #1# bei Leistung #2# nicht freigegeben', - 933 => 'Sitzplan mit ID #1# bei Leistung #2# nicht freigegeben', - 935 => 'Versicherung mit ID #1# nicht gefunden', - 940 => 'Zahlungsart fehlt', - 941 => 'Zahlungsart-ID fehlt', - 942 => 'Zahlungsart mit ID #1# nicht gültig', - 950 => 'Anmelder fehlt', - 951 => 'Teilnehmerliste fehlt', - 952 => 'Teilnehmer-ID fehlt', - 960 => 'Preisfehler: Struct #1# / Obj #2#', - 961 => 'Buchung konnte nicht gespeichert werden', - 980 => 'Reise ist fürs Internet gesperrt', - 981 => 'Reise ist nicht mehr buchbar (#1#)', - 982 => 'Optionsbuchungen nicht zugelassen', - 983 => 'Anfragebuchung nicht zugelassen', - 984 => 'Leistung mit ID #1# nicht fürs Internet buchbar', - 985 => 'Zustieg mit ID #1# nicht fürs Internet buchbar', - 999 => 'Systemfehler: #1#', - ]; -} diff --git a/src/BusProNet/Model/PersonalData.php b/src/BusProNet/Model/PersonalData.php index 57458de..4135977 100644 --- a/src/BusProNet/Model/PersonalData.php +++ b/src/BusProNet/Model/PersonalData.php @@ -56,7 +56,9 @@ class PersonalData #[Assert\Valid(groups: ['personal_data'])] public Communication $communication; + /** @var list */ public array $roles = []; + /** @var list */ public array $hotelCodes = []; public function __construct() diff --git a/src/BusProNet/Model/Pickup.php b/src/BusProNet/Model/Pickup.php index 9f66599..39c521c 100644 --- a/src/BusProNet/Model/Pickup.php +++ b/src/BusProNet/Model/Pickup.php @@ -45,6 +45,7 @@ class Pickup #[Groups(['api:single', 'snapshot'])] public ?float $priceInbound = null; + /** @var list */ #[Groups(['api:booking'])] public array $mapping = []; diff --git a/src/BusProNet/Model/Room.php b/src/BusProNet/Model/Room.php index 480aea0..a8bb544 100644 --- a/src/BusProNet/Model/Room.php +++ b/src/BusProNet/Model/Room.php @@ -64,6 +64,7 @@ class Room #[Groups(['booking'])] public ?string $board = null; + /** @var list */ #[Groups(['booking'])] public array $mapping = []; @@ -73,6 +74,7 @@ class Room #[Groups(['booking'])] public ?float $totalPrice = null; + /** @var array */ #[Groups(['booking'])] public array $individualPrice = []; diff --git a/src/BusProNet/Model/Service.php b/src/BusProNet/Model/Service.php index c5c28ab..7edd8b8 100644 --- a/src/BusProNet/Model/Service.php +++ b/src/BusProNet/Model/Service.php @@ -55,6 +55,7 @@ class Service #[Groups(['booking'])] public ?int $totalCount = null; + /** @var list */ #[Groups(['api:single', 'snapshot'])] public array $mapping = []; @@ -64,6 +65,7 @@ class Service #[Groups(['api:single', 'snapshot'])] public ?float $totalPrice = null; + /** @var array */ #[Groups(['api:single', 'snapshot'])] public array $individualPrice = []; @@ -91,6 +93,7 @@ class Service #[Groups(['api:single', 'api:list', 'snapshot'])] public ?string $ageConstraintType = null; + /** @var array|null */ #[Groups(['api:single'])] public ?array $ageConstraintMetadata = null; diff --git a/src/BusProNet/Model/Surcharge.php b/src/BusProNet/Model/Surcharge.php index 3d82074..c1ff696 100644 --- a/src/BusProNet/Model/Surcharge.php +++ b/src/BusProNet/Model/Surcharge.php @@ -13,7 +13,9 @@ namespace App\BusProNet\Model; class Surcharge { public ?string $label = null; + /** @var list */ public array $mapping = []; public ?float $totalPrice = null; + /** @var array */ public array $individualPrice = []; } diff --git a/src/BusProNet/Model/Travel.php b/src/BusProNet/Model/Travel.php index 0b6ae5d..0fdbe6f 100644 --- a/src/BusProNet/Model/Travel.php +++ b/src/BusProNet/Model/Travel.php @@ -203,7 +203,7 @@ class Travel * Maps selection group IDs to their corresponding services based on * the predefined included services mapping. * - * @return array The included services from selection groups + * @return array The included services from selection groups */ public function getIncludedServices(): array { diff --git a/src/BusProNet/Service/StatusRule/ChaperonServiceStatusRule.php b/src/BusProNet/Service/StatusRule/ChaperonServiceStatusRule.php index 28917a1..592c409 100644 --- a/src/BusProNet/Service/StatusRule/ChaperonServiceStatusRule.php +++ b/src/BusProNet/Service/StatusRule/ChaperonServiceStatusRule.php @@ -47,7 +47,7 @@ class ChaperonServiceStatusRule implements BookingStatusRuleInterface foreach ($arrayServices as $services) { foreach ($services as $service) { - if ($service instanceof Service && true === $this->containsSearchTerm($service->label)) { + if (true === $this->containsSearchTerm($service->label)) { return true; } } diff --git a/src/BusProNet/Traits/SortByPriceTrait.php b/src/BusProNet/Traits/SortByPriceTrait.php index f178d23..9a20aad 100644 --- a/src/BusProNet/Traits/SortByPriceTrait.php +++ b/src/BusProNet/Traits/SortByPriceTrait.php @@ -28,9 +28,9 @@ trait SortByPriceTrait * collections in the application. Items are sorted from lowest to highest price, * with null prices being treated as zero (appearing first). * - * @param array $items Array of objects with a price property to sort + * @param array $items Array of objects with a price property to sort * - * @return array The sorted array of objects (ascending by price) + * @return array The sorted array of objects (ascending by price) */ protected function sortByPrice(array $items): array { diff --git a/src/BusProNet/Traits/TypeConversionTrait.php b/src/BusProNet/Traits/TypeConversionTrait.php index 9b3a96b..b7d497b 100644 --- a/src/BusProNet/Traits/TypeConversionTrait.php +++ b/src/BusProNet/Traits/TypeConversionTrait.php @@ -7,6 +7,7 @@ use Carbon\Exceptions\InvalidFormatException; trait TypeConversionTrait { + /** @return list */ protected function stringToArray(?string $string, string $separator = ','): array { if (true === empty($string)) { diff --git a/src/BusProNet/XmlLoader/HotelLoader.php b/src/BusProNet/XmlLoader/HotelLoader.php index 8d50b87..051f343 100644 --- a/src/BusProNet/XmlLoader/HotelLoader.php +++ b/src/BusProNet/XmlLoader/HotelLoader.php @@ -11,6 +11,7 @@ use Symfony\Contracts\Cache\ItemInterface; class HotelLoader extends AbstractLoader { + /** @return array */ public function loadAll(?string $filename = 'hotel.xml'): array { try { diff --git a/src/BusProNet/XmlLoader/InsuranceLoader.php b/src/BusProNet/XmlLoader/InsuranceLoader.php index 89c78b1..6573d49 100644 --- a/src/BusProNet/XmlLoader/InsuranceLoader.php +++ b/src/BusProNet/XmlLoader/InsuranceLoader.php @@ -22,6 +22,7 @@ class InsuranceLoader extends AbstractLoader parent::__construct($cache, $xmlExport); } + /** @return array */ public function loadAll(?string $filename = 'versicherungen.xml'): array { try { diff --git a/src/BusProNet/XmlLoader/PickupLoader.php b/src/BusProNet/XmlLoader/PickupLoader.php index 1d0189f..f8e5084 100644 --- a/src/BusProNet/XmlLoader/PickupLoader.php +++ b/src/BusProNet/XmlLoader/PickupLoader.php @@ -10,6 +10,7 @@ use Symfony\Contracts\Cache\ItemInterface; class PickupLoader extends AbstractLoader { + /** @return array */ public function loadAll(?string $filename = 'zustiege.xml'): array { try { @@ -65,6 +66,11 @@ class PickupLoader extends AbstractLoader $travel->dropOffs = $this->patchAndOrderDropOffs($travel->dropOffs, $travel->pickups); } + /** + * @param array $pickups + * + * @return array + */ private function patchAndSortPickups(array $pickups): array { $this->enrichPickupDetails($pickups); diff --git a/src/BusProNet/XmlLoader/TravelLoader.php b/src/BusProNet/XmlLoader/TravelLoader.php index 0098fd0..04e00dd 100644 --- a/src/BusProNet/XmlLoader/TravelLoader.php +++ b/src/BusProNet/XmlLoader/TravelLoader.php @@ -51,7 +51,7 @@ class TravelLoader extends AbstractLoader * including ID, code, label, dates, and associated hotels. The mapping is * cached for 3 hours to improve performance. * - * @return array Mapping of travel IDs to their metadata and file paths + * @return array> Mapping of travel IDs to their metadata and file paths */ public function generateFilesMap(): array { @@ -74,7 +74,7 @@ class TravelLoader extends AbstractLoader $travelDataNodes = $crawler->filterXPath('//reisen/reise/termin'); $travelDataNodes->each(function (Crawler $node) use (&$mapping, $hotels, $file) { - $travelId = $node->attr('idbuspro'); + $travelId = (int) $node->attr('idbuspro'); $mapping[$travelId] = [ 'id' => $travelId, @@ -173,6 +173,7 @@ class TravelLoader extends AbstractLoader * * @throws TravelNotFoundException When travel ID is not found * @throws HotelNotInTravelException When hotel ID exists but not for this travel + * @throws \RuntimeException When cached mapping generation fails unexpectedly */ public function loadById(int $dateId, ?int $hotelId = null, ?string $filename = null): Travel { diff --git a/src/BusProNet/XmlParser/AbstractParser.php b/src/BusProNet/XmlParser/AbstractParser.php index 3c8c0e7..41f1ed8 100644 --- a/src/BusProNet/XmlParser/AbstractParser.php +++ b/src/BusProNet/XmlParser/AbstractParser.php @@ -39,6 +39,7 @@ abstract class AbstractParser return 0 < $node->count() && $this->stringToBool($node->text()); } + /** @return list */ protected function getArrayValue(Crawler $node, string $separator = ','): array { if (0 === $node->count()) { diff --git a/src/BusProNet/XmlParser/AgeConstraintParserRegistry.php b/src/BusProNet/XmlParser/AgeConstraintParserRegistry.php index 08f2ea6..1182909 100644 --- a/src/BusProNet/XmlParser/AgeConstraintParserRegistry.php +++ b/src/BusProNet/XmlParser/AgeConstraintParserRegistry.php @@ -53,6 +53,7 @@ class AgeConstraintParserRegistry return $this->mergeConstraintResults($results, $constraintData); } + /** @param list $results */ private function mergeConstraintResults(array $results, string $rawData): AgeConstraintResult { if (empty($results)) { diff --git a/src/BusProNet/XmlParser/ApiResponseParser.php b/src/BusProNet/XmlParser/ApiResponseParser.php index 788425d..8662f19 100644 --- a/src/BusProNet/XmlParser/ApiResponseParser.php +++ b/src/BusProNet/XmlParser/ApiResponseParser.php @@ -11,6 +11,7 @@ class ApiResponseParser extends AbstractParser /** * @throws ResponseParserException */ + /** @param array $additionalArgs */ public function parseXmlString(string $type, string $xml, array $additionalArgs = []): mixed { $crawler = new Crawler($xml); diff --git a/src/BusProNet/XmlParser/BookingInsurancesParser.php b/src/BusProNet/XmlParser/BookingInsurancesParser.php index d9e6488..79e5cf6 100644 --- a/src/BusProNet/XmlParser/BookingInsurancesParser.php +++ b/src/BusProNet/XmlParser/BookingInsurancesParser.php @@ -15,6 +15,7 @@ use Symfony\Component\DomCrawler\Crawler; */ class BookingInsurancesParser extends AbstractParser { + /** @return array */ public function parse(Crawler $result): array { $insurances = []; diff --git a/src/BusProNet/XmlParser/BookingParser.php b/src/BusProNet/XmlParser/BookingParser.php index 898028e..46da375 100644 --- a/src/BusProNet/XmlParser/BookingParser.php +++ b/src/BusProNet/XmlParser/BookingParser.php @@ -112,6 +112,7 @@ class BookingParser extends AbstractParser return $booking; } + /** @return array */ private function parseParticipants(Crawler $node): array { $participants = []; diff --git a/src/BusProNet/XmlParser/CrmAttributesResponseParser.php b/src/BusProNet/XmlParser/CrmAttributesResponseParser.php index 9930137..87ac380 100644 --- a/src/BusProNet/XmlParser/CrmAttributesResponseParser.php +++ b/src/BusProNet/XmlParser/CrmAttributesResponseParser.php @@ -27,7 +27,7 @@ class CrmAttributesResponseParser $result ->filterXPath('//selektionsmerkmale/selektionsgruppe') - ->each(function (Crawler $node) use (&$groups, &$roles, &$hotelCode) { + ->each(function (Crawler $node) use (&$groups, &$roles) { $group = new CrmSelectionGroup(); $group->id = (int) $node->attr('id'); $group->label = $node->attr('bezeichnung'); @@ -47,14 +47,14 @@ class CrmAttributesResponseParser $roles[] = 'ROLE_HOUSE_MANAGER'; $hotelCodes[] = $matches[1]; } - if (static::BPN_CRM_ID_ADMIN === $attribute->id && true === $attribute->selected) { + if (self::BPN_CRM_ID_ADMIN === $attribute->id && true === $attribute->selected) { $roles[] = 'ROLE_ADMIN'; $roles[] = 'ROLE_HOUSE_MANAGER'; } - if (static::BPN_CRM_ID_MANAGER === $attribute->id && true === $attribute->selected) { + if (self::BPN_CRM_ID_MANAGER === $attribute->id && true === $attribute->selected) { $roles[] = 'ROLE_MANAGER'; } - if (static::BPN_CRM_ID_TEAMER === $attribute->id && true === $attribute->selected) { + if (self::BPN_CRM_ID_TEAMER === $attribute->id && true === $attribute->selected) { $roles[] = 'ROLE_TEAMER'; } @@ -90,7 +90,7 @@ class CrmAttributesResponseParser ; if (true === in_array('ROLE_ADMIN', $roles, true)) { - $hotelCodes[] = static::BPN_DEFAULT_HOTEL_CODE; + $hotelCodes[] = self::BPN_DEFAULT_HOTEL_CODE; } $response = new CrmAttributes(); diff --git a/src/BusProNet/XmlParser/DocumentsParser.php b/src/BusProNet/XmlParser/DocumentsParser.php index cda6f5b..3ee0271 100644 --- a/src/BusProNet/XmlParser/DocumentsParser.php +++ b/src/BusProNet/XmlParser/DocumentsParser.php @@ -58,6 +58,7 @@ class DocumentsParser return base64_decode($pdfData); } + /** @return array{string, string} */ private function getFileInfo(Crawler $node): array { $pdfData = $this->decode($node->filterXPath('.//pdf')); diff --git a/src/BusProNet/XmlParser/InsuranceParser.php b/src/BusProNet/XmlParser/InsuranceParser.php index de6cfbe..4487f3e 100644 --- a/src/BusProNet/XmlParser/InsuranceParser.php +++ b/src/BusProNet/XmlParser/InsuranceParser.php @@ -37,7 +37,7 @@ class InsuranceParser extends AbstractParser // Parse all individual insurances for reference lookup $insurance = $this->parseInsuranceNode($node, false); - if (null !== $insurance && null !== $insurance->id) { + if (null !== $insurance->id) { // Set complementary flag from XML attribute $insurance->complementary = $isComplementary; @@ -53,7 +53,7 @@ class InsuranceParser extends AbstractParser $xmlContent->filterXPath('//versicherungspakete/versicherungspaket') ->each(function (Crawler $node) use (&$insurances, $individualInsurances) { $insurance = $this->parseInsuranceNode($node, true, $individualInsurances); - if (null !== $insurance && null !== $insurance->id) { + if (null !== $insurance->id) { // Parse contained insurance IDs $insurance->containedInsuranceIds = $this->parseContainedInsuranceIds($node); @@ -75,8 +75,8 @@ class InsuranceParser extends AbstractParser /** * Determines if a package contains any family insurances. * - * @param Crawler $packageNode The package XML node - * @param array $individualInsurances Parsed individual insurances for reference + * @param Crawler $packageNode The package XML node + * @param array $individualInsurances Parsed individual insurances for reference * * @return bool True if any contained insurance is a family insurance */ @@ -96,13 +96,13 @@ class InsuranceParser extends AbstractParser /** * Parses a single insurance or package node. * - * @param Crawler $node The XML node to parse - * @param bool $isPackage Whether this is a package node - * @param array $individualInsurances Individual insurances for package family detection + * @param Crawler $node The XML node to parse + * @param bool $isPackage Whether this is a package node + * @param array $individualInsurances Individual insurances for package family detection * - * @return Insurance|null The parsed insurance object + * @return Insurance The parsed insurance object */ - private function parseInsuranceNode(Crawler $node, bool $isPackage, array $individualInsurances = []): ?Insurance + private function parseInsuranceNode(Crawler $node, bool $isPackage, array $individualInsurances = []): Insurance { $insurance = new Insurance(); $insurance->package = $isPackage; @@ -159,7 +159,7 @@ class InsuranceParser extends AbstractParser * * @param Crawler $xmlContent The XML content to scan * - * @return array Array of referenced insurance IDs + * @return list Array of referenced insurance IDs */ private function collectReferencedInsuranceIds(Crawler $xmlContent): array { @@ -180,7 +180,7 @@ class InsuranceParser extends AbstractParser * * @param Crawler $node The package XML node * - * @return array Array of contained insurance IDs (always int, as they reference individual insurances) + * @return list Array of contained insurance IDs */ private function parseContainedInsuranceIds(Crawler $node): array { diff --git a/src/BusProNet/XmlParser/PickupsParser.php b/src/BusProNet/XmlParser/PickupsParser.php index 47cd0d8..2cd0de7 100644 --- a/src/BusProNet/XmlParser/PickupsParser.php +++ b/src/BusProNet/XmlParser/PickupsParser.php @@ -7,6 +7,7 @@ use Symfony\Component\DomCrawler\Crawler; class PickupsParser extends AbstractParser { + /** @return array */ public function parse(Crawler $result): array { $pickups = []; diff --git a/src/BusProNet/XmlParser/RoomsParser.php b/src/BusProNet/XmlParser/RoomsParser.php index b56fb06..deb102f 100644 --- a/src/BusProNet/XmlParser/RoomsParser.php +++ b/src/BusProNet/XmlParser/RoomsParser.php @@ -7,6 +7,7 @@ use Symfony\Component\DomCrawler\Crawler; class RoomsParser extends AbstractParser { + /** @return array */ public function parse(Crawler $result): array { $rooms = []; diff --git a/src/BusProNet/XmlParser/ServicesParser.php b/src/BusProNet/XmlParser/ServicesParser.php index 1cbb619..80d3a11 100644 --- a/src/BusProNet/XmlParser/ServicesParser.php +++ b/src/BusProNet/XmlParser/ServicesParser.php @@ -8,6 +8,7 @@ use Symfony\Component\DomCrawler\Crawler; class ServicesParser extends AbstractParser { + /** @return array */ public function parse(Crawler $result, string $category, string $source): array { $services = []; diff --git a/src/BusProNet/XmlParser/SurchargesParser.php b/src/BusProNet/XmlParser/SurchargesParser.php index d2c25af..44bc947 100644 --- a/src/BusProNet/XmlParser/SurchargesParser.php +++ b/src/BusProNet/XmlParser/SurchargesParser.php @@ -7,6 +7,7 @@ use Symfony\Component\DomCrawler\Crawler; class SurchargesParser extends AbstractParser { + /** @return list */ public function parse(Crawler $result): array { $surcharges = []; diff --git a/src/Controller/Admin/BookingEditDraftCrudController.php b/src/Controller/Admin/BookingEditDraftCrudController.php index e294280..8c922fa 100644 --- a/src/Controller/Admin/BookingEditDraftCrudController.php +++ b/src/Controller/Admin/BookingEditDraftCrudController.php @@ -19,6 +19,7 @@ use EasyCorp\Bundle\EasyAdminBundle\Router\AdminUrlGenerator; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\Routing\Attribute\Route; +/** @extends AbstractCrudController */ class BookingEditDraftCrudController extends AbstractCrudController { public function __construct( diff --git a/src/Controller/Admin/LogEntryCrudController.php b/src/Controller/Admin/LogEntryCrudController.php index fda1bd3..277aa19 100644 --- a/src/Controller/Admin/LogEntryCrudController.php +++ b/src/Controller/Admin/LogEntryCrudController.php @@ -18,6 +18,7 @@ use League\Flysystem\FilesystemException; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +/** @extends AbstractCrudController */ class LogEntryCrudController extends AbstractCrudController { public function __construct( diff --git a/src/Controller/Admin/UserCrudController.php b/src/Controller/Admin/UserCrudController.php index 77067a5..d23754e 100644 --- a/src/Controller/Admin/UserCrudController.php +++ b/src/Controller/Admin/UserCrudController.php @@ -10,6 +10,7 @@ use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController; use EasyCorp\Bundle\EasyAdminBundle\Field\DateTimeField; use EasyCorp\Bundle\EasyAdminBundle\Field\TextField; +/** @extends AbstractCrudController */ class UserCrudController extends AbstractCrudController { public static function getEntityFqcn(): string diff --git a/src/Controller/Api/PickupController.php b/src/Controller/Api/PickupController.php index b868a79..5395acd 100644 --- a/src/Controller/Api/PickupController.php +++ b/src/Controller/Api/PickupController.php @@ -8,6 +8,7 @@ use League\Flysystem\FilesystemException; use League\Flysystem\FilesystemOperator; use Psr\Log\LoggerInterface; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; +use Symfony\Component\HttpFoundation\Exception\JsonException; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -73,7 +74,7 @@ class PickupController extends AbstractController { try { $payload = $request->toArray(); - } catch (\JsonException $e) { + } catch (JsonException $e) { $this->logger->error('Failed to update pickups planning data', [ 'message' => $e->getMessage(), 'payload' => (string) $request->getContent(), diff --git a/src/Controller/Api/TravelController.php b/src/Controller/Api/TravelController.php index 9649e40..8b5a112 100644 --- a/src/Controller/Api/TravelController.php +++ b/src/Controller/Api/TravelController.php @@ -58,7 +58,7 @@ class TravelController extends AbstractController name: 'api_travel_single_code', defaults: ['hotelCode' => null], )] - public function byCode(Request $request, $dateCode, ?string $hotelCode = null): JsonResponse + public function byCode(Request $request, string $dateCode, ?string $hotelCode = null): JsonResponse { // sanitize date code by removing potential dividers $dateCode = (new DateCodeUtility())->sanitize($dateCode); diff --git a/src/Controller/Api/UserinfoController.php b/src/Controller/Api/UserinfoController.php index 9e2b395..1e83a07 100644 --- a/src/Controller/Api/UserinfoController.php +++ b/src/Controller/Api/UserinfoController.php @@ -66,6 +66,11 @@ class UserinfoController extends AbstractController } } + /** + * @param list $scopes + * + * @return array + */ private function getClaims(PersonalData $data, array $scopes): array { // get all available claims diff --git a/src/Controller/Booking/Create/Step2Controller.php b/src/Controller/Booking/Create/Step2Controller.php index 2774e5c..5dc97ce 100644 --- a/src/Controller/Booking/Create/Step2Controller.php +++ b/src/Controller/Booking/Create/Step2Controller.php @@ -132,6 +132,7 @@ class Step2Controller extends AbstractController } } + /** @param array $roomSelections */ private function calculateParticipantsCount(array $roomSelections, Travel $travelData): int { $participantsCount = 0; diff --git a/src/Controller/Booking/Create/Step2ParticipantController.php b/src/Controller/Booking/Create/Step2ParticipantController.php index c98a41e..10746fb 100644 --- a/src/Controller/Booking/Create/Step2ParticipantController.php +++ b/src/Controller/Booking/Create/Step2ParticipantController.php @@ -124,6 +124,7 @@ class Step2ParticipantController extends AbstractController ); } + /** @param FormInterface $form */ private function renderParticipantForm( FormInterface $form, int $index, @@ -157,6 +158,7 @@ class Step2ParticipantController extends AbstractController return $bookingDto; } + /** @return FormInterface */ private function createParticipantForm(BookingDto $bookingDto, int $index): FormInterface { $wrapper = $this->participantFormSupportService->createParticipantEditDto($bookingDto, $index); diff --git a/src/Controller/Booking/Create/Step3Controller.php b/src/Controller/Booking/Create/Step3Controller.php index 16dec89..55988d9 100644 --- a/src/Controller/Booking/Create/Step3Controller.php +++ b/src/Controller/Booking/Create/Step3Controller.php @@ -220,6 +220,7 @@ class Step3Controller extends AbstractController /** * Renders the step 3 form with standard template variables. */ + /** @param FormInterface $form */ private function renderStepForm(BookingDto $bookingCreateDto, FormInterface $form): Response { $context = $this->createContextFactory->create( diff --git a/src/Controller/Booking/Create/Step4Controller.php b/src/Controller/Booking/Create/Step4Controller.php index f0926ae..ff5057f 100644 --- a/src/Controller/Booking/Create/Step4Controller.php +++ b/src/Controller/Booking/Create/Step4Controller.php @@ -177,6 +177,9 @@ class Step4Controller extends AbstractController /** * Renders the step 4 form with standard template variables. */ + /** + * @param FormInterface $form + */ private function renderStepForm( BookingDto $bookingCreateDto, FormInterface $form, diff --git a/src/Controller/Booking/Create/SuccessController.php b/src/Controller/Booking/Create/SuccessController.php index a520f06..047c074 100644 --- a/src/Controller/Booking/Create/SuccessController.php +++ b/src/Controller/Booking/Create/SuccessController.php @@ -8,6 +8,7 @@ use App\Service\BookingSessionManager; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface; use Symfony\Component\Routing\Attribute\Route; /** @@ -23,11 +24,17 @@ class SuccessController extends AbstractController #[Route('/bookings/create/success', name: 'app_booking_create_success')] public function success(Request $request): Response { - $flashBag = $request->getSession()->getFlashBag(); + $returnUrl = $this->bookingSessionService->getReturnUrl($request); + $session = $request->getSession(); + + if (!$session instanceof FlashBagAwareSessionInterface) { + return $this->redirect($returnUrl); + } + + $flashBag = $session->getFlashBag(); $bookingNumber = $flashBag->get('booking_number')[0] ?? null; $bookingTotal = $flashBag->get('booking_total')[0] ?? null; $travelName = $flashBag->get('booking_travel_name')[0] ?? null; - $returnUrl = $this->bookingSessionService->getReturnUrl($request); // Redirect to return URL if no booking number (direct access or refresh) if (null === $bookingNumber) { diff --git a/src/Controller/Booking/DownloadController.php b/src/Controller/Booking/DownloadController.php index 6d24ef1..6c31253 100644 --- a/src/Controller/Booking/DownloadController.php +++ b/src/Controller/Booking/DownloadController.php @@ -49,6 +49,7 @@ class DownloadController extends AbstractController $type = match ($fileType) { 'documents' => 'Dokumentdruck', 'invoice' => 'Vorgangdruck', + default => throw new \InvalidArgumentException(sprintf('Unknown file type: %s', $fileType)), }; $this->logger->info('Initiated document download', [ diff --git a/src/Controller/Booking/Traits/BookingCreateTrait.php b/src/Controller/Booking/Traits/BookingCreateTrait.php index 3a30b7b..b12f6c5 100644 --- a/src/Controller/Booking/Traits/BookingCreateTrait.php +++ b/src/Controller/Booking/Traits/BookingCreateTrait.php @@ -52,6 +52,9 @@ trait BookingCreateTrait /** * Handles API errors by logging and adding a flash message. */ + /** + * @param array $context + */ private function handleApiError( LoggerInterface $logger, string $logMessage, diff --git a/src/Controller/Booking/Traits/BookingExceptionHandlerTrait.php b/src/Controller/Booking/Traits/BookingExceptionHandlerTrait.php index 9861c0e..10945b0 100644 --- a/src/Controller/Booking/Traits/BookingExceptionHandlerTrait.php +++ b/src/Controller/Booking/Traits/BookingExceptionHandlerTrait.php @@ -5,9 +5,7 @@ declare(strict_types=1); namespace App\Controller\Booking\Traits; use App\Exception\BookingSessionNotFoundException; -use App\Exception\HotelNotFoundException; use App\Exception\HotelNotInTravelException; -use App\Exception\NoRoomsAvailableException; use App\Exception\TravelNotFoundException; use App\Form\Model\BookingDto; use App\Service\BookingSessionManager; @@ -33,25 +31,9 @@ trait BookingExceptionHandlerTrait { try { return $bookingSessionService->getOrCreateBookingCreateDto($request); - } catch (BookingSessionNotFoundException $e) { + } catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $e) { $this->addFlash('error', 'Deine Buchungssitzung ist abgelaufen. Bitte starte eine neue Buchung.'); - return $this->redirectToRoute('app_booking_create_error'); - } catch (TravelNotFoundException $e) { - $this->addFlash('error', 'Die angeforderte Reise wurde nicht gefunden.'); - - return $this->redirectToRoute('app_booking_create_error'); - } catch (HotelNotFoundException $e) { - $this->addFlash('error', 'Das angeforderte Hotel wurde nicht gefunden.'); - - return $this->redirectToRoute('app_booking_create_error'); - } catch (HotelNotInTravelException $e) { - $this->addFlash('error', 'Das Hotel ist für diese Reise nicht verfügbar.'); - - return $this->redirectToRoute('app_booking_create_error'); - } catch (NoRoomsAvailableException $e) { - $this->addFlash('error', 'Für diese Reise sind aktuell keine Zimmer verfügbar.'); - return $this->redirectToRoute('app_booking_create_error'); } } @@ -66,7 +48,7 @@ trait BookingExceptionHandlerTrait { try { return $bookingSessionService->getOrCreateBookingCreateDto($request); - } catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotFoundException|HotelNotInTravelException|NoRoomsAvailableException $e) { + } catch (BookingSessionNotFoundException|TravelNotFoundException|HotelNotInTravelException $e) { return new Response('', 400); } } diff --git a/src/Form/AddressType.php b/src/Form/AddressType.php index c54a331..52039e6 100644 --- a/src/Form/AddressType.php +++ b/src/Form/AddressType.php @@ -11,6 +11,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType
*/ class AddressType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/BankAccountType.php b/src/Form/BankAccountType.php index efdab67..2dbe8f0 100644 --- a/src/Form/BankAccountType.php +++ b/src/Form/BankAccountType.php @@ -11,6 +11,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class BankAccountType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/BodyDimensionsType.php b/src/Form/BodyDimensionsType.php index 81df486..f923b1c 100644 --- a/src/Form/BodyDimensionsType.php +++ b/src/Form/BodyDimensionsType.php @@ -10,6 +10,7 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class BodyDimensionsType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/BookingCreateStep1Type.php b/src/Form/BookingCreateStep1Type.php index 11df1fb..8ad7c3c 100644 --- a/src/Form/BookingCreateStep1Type.php +++ b/src/Form/BookingCreateStep1Type.php @@ -8,6 +8,7 @@ use Symfony\Component\Form\Extension\Core\Type\CollectionType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class BookingCreateStep1Type extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/BookingCreateStep2Type.php b/src/Form/BookingCreateStep2Type.php index 0328e6b..a4685e8 100644 --- a/src/Form/BookingCreateStep2Type.php +++ b/src/Form/BookingCreateStep2Type.php @@ -17,6 +17,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; * in separate forms. This form validates the complete BookingDto before proceeding * to Step 3, ensuring all participants have valid and complete data. */ +/** @extends AbstractType */ class BookingCreateStep2Type extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/BookingCreateStep3Type.php b/src/Form/BookingCreateStep3Type.php index 2d2bedf..09058b3 100644 --- a/src/Form/BookingCreateStep3Type.php +++ b/src/Form/BookingCreateStep3Type.php @@ -12,8 +12,10 @@ use Symfony\Component\Form\Extension\Core\Type\ChoiceType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; +use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class BookingCreateStep3Type extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void @@ -76,7 +78,8 @@ class BookingCreateStep3Type extends AbstractType /** * Adds the bank account field to the form. */ - private function addBankAccountField($form): void + /** @param FormInterface $form */ + private function addBankAccountField(FormInterface $form): void { $form->add('bankAccount', BankAccountType::class, [ 'label' => false, diff --git a/src/Form/BookingCreateStep4Type.php b/src/Form/BookingCreateStep4Type.php index 84d0039..ac03e51 100644 --- a/src/Form/BookingCreateStep4Type.php +++ b/src/Form/BookingCreateStep4Type.php @@ -11,6 +11,7 @@ use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; use Symfony\Component\Validator\Constraints as Assert; +/** @extends AbstractType */ class BookingCreateStep4Type extends AbstractType { public function __construct( diff --git a/src/Form/BookingEditType.php b/src/Form/BookingEditType.php index e465572..f4db55c 100644 --- a/src/Form/BookingEditType.php +++ b/src/Form/BookingEditType.php @@ -17,6 +17,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; * This form validates the complete BookingDto before allowing updates, * ensuring all participants have valid and complete data. */ +/** @extends AbstractType */ class BookingEditType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/BookingParticipantType.php b/src/Form/BookingParticipantType.php index 34ca7d5..0ac71fc 100644 --- a/src/Form/BookingParticipantType.php +++ b/src/Form/BookingParticipantType.php @@ -24,6 +24,7 @@ use Symfony\Component\Form\FormEvents; use Symfony\Component\Form\FormInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class BookingParticipantType extends AbstractType { private FieldStateProviderInterface $fieldStateProvider; @@ -80,10 +81,6 @@ class BookingParticipantType extends AbstractType /** @var ParticipantEditDto $data */ $data = $form->getData(); - if (null === $data || null === $data->participant) { - return; - } - // Process all field handlers for this participant and sync submitted data $syncedData = $this->fieldHandlerRegistry->processFieldsForParticipantAndSync( $submittedData, @@ -110,11 +107,7 @@ class BookingParticipantType extends AbstractType $form = $event->getForm(); // Use bookingContext from wrapper DTO or fallback to passed option - $bookingDto = $data->bookingContext ?? $bookingContext ?? $this->fieldStateProvider->getBookingDtoFromForm($form); - - if (null === $bookingDto) { - return; - } + $bookingDto = $data->bookingContext; // Add base fields with states applied $this->addBaseFields($form, $bookingDto, $data->participant->index); @@ -134,16 +127,8 @@ class BookingParticipantType extends AbstractType /** @var ParticipantEditDto $data */ $data = $form->getData(); - if (null === $data || null === $data->participant) { - return; - } - // Use bookingContext from wrapper DTO or fallback to passed option - $bookingDto = $data->bookingContext ?? $bookingContext ?? $this->fieldStateProvider->getBookingDtoFromForm($form); - - if (null === $bookingDto) { - return; - } + $bookingDto = $data->bookingContext; // Rebuild all fields with updated states based on submitted data $this->rebuildFieldsWithStates($form, $bookingDto, $data->participant->index, $submittedData); @@ -154,6 +139,7 @@ class BookingParticipantType extends AbstractType /** * Adds base fields to the form with field states applied. */ + /** @param FormInterface $form */ private function addBaseFields(FormInterface $form, BookingDto $bookingDto, int $participantIndex): void { // Get field states for base fields @@ -280,6 +266,10 @@ class BookingParticipantType extends AbstractType * @param int $participantIndex The participant index * @param array $submittedData Submitted form data for state calculation */ + /** + * @param FormInterface $form + * @param array $submittedData + */ private function removeExcludedFields(FormInterface $form, BookingDto $bookingDto, int $participantIndex, array &$submittedData = []): void { foreach (ParticipantDto::DYNAMIC_FIELDS as $fieldName) { @@ -296,6 +286,10 @@ class BookingParticipantType extends AbstractType /** * Rebuilds all fields with updated states based on submitted data. */ + /** + * @param FormInterface $form + * @param array $submittedData + */ private function rebuildFieldsWithStates(FormInterface $form, BookingDto $bookingDto, int $participantIndex, array &$submittedData): void { // First, remove fields that should be excluded entirely @@ -326,6 +320,7 @@ class BookingParticipantType extends AbstractType /** * Adds all configured dynamic fields to the form with state conditions applied. */ + /** @param FormInterface $form */ private function addDynamicFields(FormInterface $form, BookingDto $bookingDto, int $participantIndex): void { $dynamicFields = [ diff --git a/src/Form/DataTransformer/RoomSelectionToIdTransformer.php b/src/Form/DataTransformer/RoomSelectionToIdTransformer.php index c0df431..0a30674 100644 --- a/src/Form/DataTransformer/RoomSelectionToIdTransformer.php +++ b/src/Form/DataTransformer/RoomSelectionToIdTransformer.php @@ -16,12 +16,12 @@ use Symfony\Component\Form\Exception\TransformationFailedException; * the full object (including price) via choiceData while the form binds * the scalar ID to the participant's assignedRoomId property. * - * @implements DataTransformerInterface + * @implements DataTransformerInterface */ class RoomSelectionToIdTransformer implements DataTransformerInterface { /** - * @param RoomSelectionDto[] $roomSelections Available room selections for reverse lookup + * @param array $roomSelections Available room selections for reverse lookup */ public function __construct( private readonly array $roomSelections, @@ -29,11 +29,8 @@ class RoomSelectionToIdTransformer implements DataTransformerInterface } /** - * Transforms an integer room ID to a RoomSelectionDto for form display. - * - * @param int|null $value The room ID from the model - * - * @return RoomSelectionDto|null The matching RoomSelectionDto or null + * Converts the persisted room id from the model into the matching + * RoomSelectionDto so the form can render labels and pricing details. */ public function transform(mixed $value): ?RoomSelectionDto { @@ -51,29 +48,28 @@ class RoomSelectionToIdTransformer implements DataTransformerInterface } /** - * Transforms a RoomSelectionDto back to an integer room ID for the model. + * Converts the submitted choice value back into the model's integer room id. * - * @param RoomSelectionDto|null $value The selected RoomSelectionDto from the form - * - * @return int|null The room ID or null - * - * @throws TransformationFailedException If an unexpected value type is received + * ChoiceType submits the selected room id as a scalar, so this method accepts + * an empty value, an int, or a digit-only string and rejects everything else. */ public function reverseTransform(mixed $value): ?int { - if (null === $value) { + if (null === $value || '' === $value) { return null; } - if ($value instanceof RoomSelectionDto) { - return $value->id; + if (is_int($value)) { + return $value; } - // Handle case where form submits scalar ID directly - if (is_int($value) || is_string($value)) { + if (is_string($value) && ctype_digit($value)) { return (int) $value; } - throw new TransformationFailedException(sprintf('Expected RoomSelectionDto, int, or null, got %s', get_debug_type($value))); + throw new TransformationFailedException(sprintf( + 'Invalid room id value: %s', + get_debug_type($value) + )); } } diff --git a/src/Form/Model/BankAccountDto.php b/src/Form/Model/BankAccountDto.php index c2a6285..7ccda63 100644 --- a/src/Form/Model/BankAccountDto.php +++ b/src/Form/Model/BankAccountDto.php @@ -34,9 +34,9 @@ class BankAccountDto #[Assert\IsTrue(message: 'Bitte akzeptiere das SEPA-Mandat.')] public bool $sepaMandateAccepted = false; - public static function fromBankAccount(BankAccount $bankAccount): static + public static function fromBankAccount(BankAccount $bankAccount): self { - $instance = new static(); + $instance = new self(); $instance->iban = $bankAccount->iban; $instance->accountHolder = $bankAccount->holder; $instance->bankName = $bankAccount->bankName; diff --git a/src/Form/Model/BookingDto.php b/src/Form/Model/BookingDto.php index c231598..59c4060 100644 --- a/src/Form/Model/BookingDto.php +++ b/src/Form/Model/BookingDto.php @@ -153,6 +153,7 @@ class BookingDto }); } + /** @return array */ public function getParticipants(): array { return $this->participants; diff --git a/src/Form/Model/ParticipantDto.php b/src/Form/Model/ParticipantDto.php index 0ca1f86..f37d70c 100644 --- a/src/Form/Model/ParticipantDto.php +++ b/src/Form/Model/ParticipantDto.php @@ -88,19 +88,27 @@ class ParticipantDto )] public ?string $remarksRoom = null; + /** @var list */ public array $courses = []; + /** @var list */ public array $additionalServices = []; + /** @var list */ public array $autoBookOptOutServiceIds = []; + /** @var list */ public array $autoBookOptOutSkiPassIds = []; + /** @var list */ public array $autoBookOptOutBoardIds = []; + /** @var list */ public array $autoBookOptOutRentalIds = []; // Note: Ski pass validation is conditional - see ParticipantEditDto::validateSkiPassRequired() // Babies (0-2 years) are exempt from ski pass requirement public ?Service $skiPass = null; + /** @var list */ public array $board = []; public ?Service $veg = null; + /** @var list */ public array $rentals = []; public ?Service $rentalInsurance = null; @@ -190,9 +198,9 @@ class ParticipantDto $this->address = new Address(); } - public static function fromPersonalData(PersonalData $personalData): static + public static function fromPersonalData(PersonalData $personalData): self { - $instance = new static(); + $instance = new self(); $instance->status = $personalData->status; $instance->addressId = $personalData->addressId; @@ -203,15 +211,15 @@ class ParticipantDto $instance->title = $personalData->title; $instance->gender = $personalData->gender; $instance->nationality = $personalData->nationality ?: 'D'; - $instance->email = $personalData->communication?->email; - $instance->mobile = $personalData->communication?->mobile; + $instance->email = $personalData->communication->email; + $instance->mobile = $personalData->communication->mobile; $instance->dateOfBirth = $personalData->dateOfBirth; $instance->height = $personalData->height; $instance->weight = $personalData->weight; $instance->shoeSize = $personalData->shoeSize; // Clone address to prevent shared object references that could cause mutations - $instance->address = null !== $personalData->address ? clone $personalData->address : null; + $instance->address = clone $personalData->address; $instance->remarksRoom = $personalData->remarksRoom; $instance->licensePlate = $personalData->licensePlate; @@ -292,7 +300,7 @@ class ParticipantDto */ public function getInsurancePrice(): float { - return $this->insurance?->price ?? 0.0; + return $this->insurance->price ?? 0.0; } /** diff --git a/src/Form/PaymentType.php b/src/Form/PaymentType.php index f0f2f24..a63d4d2 100644 --- a/src/Form/PaymentType.php +++ b/src/Form/PaymentType.php @@ -14,6 +14,7 @@ use Symfony\Component\Form\FormEvent; use Symfony\Component\Form\FormEvents; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class PaymentType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/PersonalDataType.php b/src/Form/PersonalDataType.php index 315a86f..63db480 100644 --- a/src/Form/PersonalDataType.php +++ b/src/Form/PersonalDataType.php @@ -12,6 +12,7 @@ use Symfony\Component\Form\Extension\Core\Type\EmailType; use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; +/** @extends AbstractType */ class PersonalDataType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/RegistrationType.php b/src/Form/RegistrationType.php index 3ced087..50f6648 100644 --- a/src/Form/RegistrationType.php +++ b/src/Form/RegistrationType.php @@ -10,6 +10,7 @@ use Symfony\Component\Form\Extension\Core\Type\TextType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class RegistrationType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/RoomAssignmentType.php b/src/Form/RoomAssignmentType.php index 103f580..46b4699 100644 --- a/src/Form/RoomAssignmentType.php +++ b/src/Form/RoomAssignmentType.php @@ -19,6 +19,7 @@ use Symfony\Component\OptionsResolver\OptionsResolver; * RoomSelectionDto objects (for template access to price data) and integer IDs * (for the participant's assignedRoomId property). */ +/** @extends AbstractType */ class RoomAssignmentType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/RoomSelectType.php b/src/Form/RoomSelectType.php index 94e7a83..0bbfcc5 100644 --- a/src/Form/RoomSelectType.php +++ b/src/Form/RoomSelectType.php @@ -13,6 +13,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class RoomSelectType extends AbstractType { public function buildForm(FormBuilderInterface $builder, array $options): void diff --git a/src/Form/Service/Abstract/AbstractFieldOptionsProvider.php b/src/Form/Service/Abstract/AbstractFieldOptionsProvider.php index 855fe29..7cf6c99 100644 --- a/src/Form/Service/Abstract/AbstractFieldOptionsProvider.php +++ b/src/Form/Service/Abstract/AbstractFieldOptionsProvider.php @@ -36,10 +36,10 @@ abstract class AbstractFieldOptionsProvider implements FieldOptionsProviderInter * used when building the form field. These options are merged with any * static options defined in the form type. * - * @param string $fieldName The name of the field to configure - * @param BookingDto $bookingDto The current booking data for context (create or edit) - * @param int $participantIndex The index of the participant being configured - * @param array $options Additional options to customize field behavior + * @param string $fieldName The name of the field to configure + * @param BookingDto $bookingDto The current booking data for context (create or edit) + * @param int $participantIndex The index of the participant being configured + * @param array $options Additional options to customize field behavior * * @return array Symfony form field options, or empty array if field not supported */ diff --git a/src/Form/Service/Condition/BulkInsuranceBookingCondition.php b/src/Form/Service/Condition/BulkInsuranceBookingCondition.php index 8c0df43..6f172cd 100644 --- a/src/Form/Service/Condition/BulkInsuranceBookingCondition.php +++ b/src/Form/Service/Condition/BulkInsuranceBookingCondition.php @@ -79,6 +79,7 @@ class BulkInsuranceBookingCondition implements FieldConditionInterface /** * Gets the bulk insurance booking flag value from form data or participant DTO. */ + /** @param array $formData */ private function getBulkInsuranceBookingValue(array $formData, object $applicant): bool { // First check form data (for fresh submissions) diff --git a/src/Form/Service/Condition/CompositeCondition.php b/src/Form/Service/Condition/CompositeCondition.php index 4056c5f..c93ea5d 100644 --- a/src/Form/Service/Condition/CompositeCondition.php +++ b/src/Form/Service/Condition/CompositeCondition.php @@ -168,6 +168,7 @@ class CompositeCondition implements FieldConditionInterface * Returns false as soon as any condition evaluates to false, * avoiding unnecessary evaluation of remaining conditions. */ + /** @param array $formData */ private function evaluateAnd(BookingDto $bookingDto, int $participantIndex, array $formData): bool { foreach ($this->conditions as $condition) { @@ -185,6 +186,7 @@ class CompositeCondition implements FieldConditionInterface * Returns true as soon as any condition evaluates to true, * avoiding unnecessary evaluation of remaining conditions. */ + /** @param array $formData */ private function evaluateOr(BookingDto $bookingDto, int $participantIndex, array $formData): bool { foreach ($this->conditions as $condition) { @@ -199,6 +201,7 @@ class CompositeCondition implements FieldConditionInterface /** * Evaluates NOT logic by inverting the result of the single condition. */ + /** @param array $formData */ private function evaluateNot(BookingDto $bookingDto, int $participantIndex, array $formData): bool { return !$this->conditions[0]->evaluate($bookingDto, $participantIndex, $formData); @@ -219,6 +222,7 @@ class CompositeCondition implements FieldConditionInterface /** * Validates condition count based on operator requirements. */ + /** @param list $conditions */ private function validateConditionCount(string $operator, array $conditions): void { $conditionCount = count($conditions); diff --git a/src/Form/Service/Condition/FieldValueCondition.php b/src/Form/Service/Condition/FieldValueCondition.php index 45a5e57..7d6fbac 100644 --- a/src/Form/Service/Condition/FieldValueCondition.php +++ b/src/Form/Service/Condition/FieldValueCondition.php @@ -175,6 +175,7 @@ class FieldValueCondition implements FieldConditionInterface /** * Retrieves field value from form data or participant data. */ + /** @param array $formData */ private function getFieldValue(array $formData, int $participantIndex, BookingDto $bookingDto): mixed { // First check participant-specific form data @@ -212,6 +213,7 @@ class FieldValueCondition implements FieldConditionInterface /** * Checks if field value is in array of expected values. */ + /** @param list $expectedValues */ private function compareIn(mixed $fieldValue, array $expectedValues): bool { foreach ($expectedValues as $expectedValue) { diff --git a/src/Form/Service/Condition/RentalInsuranceAvailableCondition.php b/src/Form/Service/Condition/RentalInsuranceAvailableCondition.php index 978a8c0..ea3cf9b 100644 --- a/src/Form/Service/Condition/RentalInsuranceAvailableCondition.php +++ b/src/Form/Service/Condition/RentalInsuranceAvailableCondition.php @@ -28,10 +28,6 @@ class RentalInsuranceAvailableCondition implements FieldConditionInterface */ public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool { - if (null === $bookingDto->travel) { - return false; - } - $services = $bookingDto->travel->getAdditionalServicesBySubTypes( Constants::TOKEN_RENTAL_INSURANCE, true diff --git a/src/Form/Service/Condition/RentalSelectionCondition.php b/src/Form/Service/Condition/RentalSelectionCondition.php index 708c317..e77447e 100644 --- a/src/Form/Service/Condition/RentalSelectionCondition.php +++ b/src/Form/Service/Condition/RentalSelectionCondition.php @@ -49,12 +49,7 @@ class RentalSelectionCondition implements FieldConditionInterface if (null !== $participant) { $rentals = $participant->rentals; if (false === empty($rentals)) { - // Check if any rental services are actually selected - foreach ($rentals as $rental) { - if ($rental instanceof Service) { - return true; - } - } + return true; } } diff --git a/src/Form/Service/Condition/RoomSelectionCondition.php b/src/Form/Service/Condition/RoomSelectionCondition.php index 3631c47..934a9f1 100644 --- a/src/Form/Service/Condition/RoomSelectionCondition.php +++ b/src/Form/Service/Condition/RoomSelectionCondition.php @@ -17,9 +17,9 @@ use App\Form\Service\Contract\FieldConditionInterface; */ class RoomSelectionCondition implements FieldConditionInterface { - private const MATCH_MODE_EXACT = 'exact'; private const MATCH_MODE_PREFIX = 'prefix'; + /** @var list */ private array $requiredRoomCodes; private string $matchMode; diff --git a/src/Form/Service/Condition/ServiceSubTypeCondition.php b/src/Form/Service/Condition/ServiceSubTypeCondition.php index ce23887..dcbb2d7 100644 --- a/src/Form/Service/Condition/ServiceSubTypeCondition.php +++ b/src/Form/Service/Condition/ServiceSubTypeCondition.php @@ -161,6 +161,7 @@ class ServiceSubTypeCondition implements FieldConditionInterface /** * Retrieves service from form data or participant data. */ + /** @param array $formData */ private function getService(array $formData, int $participantIndex, BookingDto $bookingDto): ?Service { // First check participant-specific form data @@ -185,6 +186,7 @@ class ServiceSubTypeCondition implements FieldConditionInterface /** * Checks if sub-type is in the expected array. */ + /** @param string|list $expectedSubTypes */ private function isSubTypeIn(string $actualSubType, string|array $expectedSubTypes): bool { if (is_string($expectedSubTypes)) { @@ -214,6 +216,7 @@ class ServiceSubTypeCondition implements FieldConditionInterface /** * Validates expected sub-type based on operator requirements. */ + /** @param string|list $expectedSubType */ private function validateExpectedSubType(string $operator, string|array $expectedSubType): void { if (in_array($operator, [self::OPERATOR_IN, self::OPERATOR_NOT_IN], true) && !is_array($expectedSubType)) { diff --git a/src/Form/Service/Condition/SkiPassSelectionCondition.php b/src/Form/Service/Condition/SkiPassSelectionCondition.php index 4bbaa34..8a6077a 100644 --- a/src/Form/Service/Condition/SkiPassSelectionCondition.php +++ b/src/Form/Service/Condition/SkiPassSelectionCondition.php @@ -4,7 +4,6 @@ declare(strict_types=1); namespace App\Form\Service\Condition; -use App\BusProNet\Model\Service; use App\Form\Model\BookingDto; use App\Form\Service\Contract\FieldConditionInterface; @@ -39,7 +38,7 @@ class SkiPassSelectionCondition implements FieldConditionInterface // First check submitted form data for skipass selection if (isset($formData['participants'][$participantIndex]['skiPass'])) { $selectedSkiPass = $formData['participants'][$participantIndex]['skiPass']; - if (null !== $selectedSkiPass && '' !== $selectedSkiPass) { + if ('' !== $selectedSkiPass) { return true; } } @@ -47,10 +46,7 @@ class SkiPassSelectionCondition implements FieldConditionInterface // Then check participant DTO data for existing skipass selection $participant = $bookingDto->getParticipant($participantIndex); if (null !== $participant && null !== $participant->skiPass) { - // Check if skipass is actually a Service object - if ($participant->skiPass instanceof Service) { - return true; - } + return true; } return false; diff --git a/src/Form/Service/Contract/FieldOptionsProviderInterface.php b/src/Form/Service/Contract/FieldOptionsProviderInterface.php index 64924bf..418c3fe 100644 --- a/src/Form/Service/Contract/FieldOptionsProviderInterface.php +++ b/src/Form/Service/Contract/FieldOptionsProviderInterface.php @@ -41,10 +41,10 @@ interface FieldOptionsProviderInterface * used when building the form field. These options are merged with any * static options defined in the form type. * - * @param string $fieldName The name of the field to configure - * @param BookingDto $bookingDto The current booking data for context (create or edit) - * @param int $participantIndex The index of the participant being configured - * @param array $options Additional options to customize field behavior + * @param string $fieldName The name of the field to configure + * @param BookingDto $bookingDto The current booking data for context (create or edit) + * @param int $participantIndex The index of the participant being configured + * @param array $options Additional options to customize field behavior * * @return array Symfony form field options, or empty array if field not supported */ diff --git a/src/Form/Service/Contract/FieldStateProviderInterface.php b/src/Form/Service/Contract/FieldStateProviderInterface.php index 937254a..a324230 100644 --- a/src/Form/Service/Contract/FieldStateProviderInterface.php +++ b/src/Form/Service/Contract/FieldStateProviderInterface.php @@ -5,6 +5,7 @@ declare(strict_types=1); namespace App\Form\Service\Contract; use App\Form\Model\BookingDto; +use Symfony\Component\Form\FormInterface; /** * Interface for providing dynamic field state based on conditions. @@ -30,6 +31,15 @@ use App\Form\Model\BookingDto; */ interface FieldStateProviderInterface { + /** + * Gets the BookingDto from the root of the form tree. + * + * @param FormInterface $form The form to start traversing from + * + * @return BookingDto|null The booking DTO or null if not found + */ + public function getBookingDtoFromForm(FormInterface $form): ?BookingDto; + /** * Determines whether a field should be included in the form at all. * diff --git a/src/Form/Service/ParticipantAdditionalServicesFieldHandler.php b/src/Form/Service/ParticipantAdditionalServicesFieldHandler.php index b996a15..1b4ee79 100644 --- a/src/Form/Service/ParticipantAdditionalServicesFieldHandler.php +++ b/src/Form/Service/ParticipantAdditionalServicesFieldHandler.php @@ -131,6 +131,10 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField $participant->additionalServices = $validSelections; } + /** + * @param array $availableServices + * @param list $validSelections + */ private function updateAutoBookOptOutServices( ParticipantDto $participant, array $availableServices, @@ -140,10 +144,6 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField ): void { $currentlySelectedAutoBookIds = []; foreach ($participant->additionalServices as $selectedService) { - if (false === $selectedService instanceof Service) { - continue; - } - if (null === $selectedService->id || true === $selectedService->mandatory || false === $selectedService->autoBook) { continue; } @@ -186,12 +186,12 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField * and the participant's age constraints. Services that are no longer available * or appropriate for the participant's age are filtered out. * - * @param array $selectedServices List of currently selected services - * @param array $availableServices List of all available additional services - * @param BookingDto $bookingDto The booking DTO for context - * @param int $participantIndex The participant index for age evaluation + * @param list $selectedServices List of currently selected services + * @param array $availableServices List of all available additional services + * @param BookingDto $bookingDto The booking DTO for context + * @param int $participantIndex The participant index for age evaluation * - * @return array Filtered array of valid service selections + * @return list Filtered array of valid service selections */ private function filterValidServiceSelections( array $selectedServices, @@ -220,10 +220,10 @@ class ParticipantAdditionalServicesFieldHandler extends AbstractParticipantField * This method checks if a selected service exists in the available services * and meets the age constraints for the current participant. * - * @param mixed $selectedService The selected service to validate - * @param array $availableServices Array of available services - * @param BookingDto $bookingDto The booking DTO for context - * @param int $participantIndex The participant index for age evaluation + * @param mixed $selectedService The selected service to validate + * @param array $availableServices Array of available services + * @param BookingDto $bookingDto The booking DTO for context + * @param int $participantIndex The participant index for age evaluation * * @return bool True if the service is valid for the participant, false otherwise */ diff --git a/src/Form/Service/ParticipantBoardFieldHandler.php b/src/Form/Service/ParticipantBoardFieldHandler.php index f5c77ee..1420ffc 100644 --- a/src/Form/Service/ParticipantBoardFieldHandler.php +++ b/src/Form/Service/ParticipantBoardFieldHandler.php @@ -89,6 +89,10 @@ class ParticipantBoardFieldHandler extends AbstractParticipantFieldHandler $participant->board = $validSelections; } + /** + * @param array $availableBoard + * @param list $validSelections + */ private function updateAutoBookOptOutBoard( ParticipantDto $participant, array $availableBoard, @@ -98,10 +102,6 @@ class ParticipantBoardFieldHandler extends AbstractParticipantFieldHandler ): void { $currentlySelectedAutoBookIds = []; foreach ($participant->board as $selectedService) { - if (false === $selectedService instanceof Service) { - continue; - } - if (null === $selectedService->id || true === $selectedService->mandatory || false === $selectedService->autoBook) { continue; } @@ -135,6 +135,12 @@ class ParticipantBoardFieldHandler extends AbstractParticipantFieldHandler )); } + /** + * @param list $selectedServices + * @param array $availableServices + * + * @return list + */ private function filterValidServiceSelections( array $selectedServices, array $availableServices, @@ -156,6 +162,9 @@ class ParticipantBoardFieldHandler extends AbstractParticipantFieldHandler return $validSelections; } + /** + * @param array $availableServices + */ private function isServiceValidForParticipant( mixed $selectedService, array $availableServices, diff --git a/src/Form/Service/ParticipantCoursesFieldHandler.php b/src/Form/Service/ParticipantCoursesFieldHandler.php index d83ab82..256f11f 100644 --- a/src/Form/Service/ParticipantCoursesFieldHandler.php +++ b/src/Form/Service/ParticipantCoursesFieldHandler.php @@ -114,12 +114,12 @@ class ParticipantCoursesFieldHandler extends AbstractParticipantFieldHandler /** * Filters course selections to keep only those valid for the participant's age. * - * @param array $selectedServices List of currently selected courses - * @param array $availableServices List of all available courses - * @param BookingDto $bookingDto The booking DTO for context - * @param int $participantIndex The participant index for age evaluation + * @param list $selectedServices List of currently selected courses + * @param array $availableServices List of all available courses + * @param BookingDto $bookingDto The booking DTO for context + * @param int $participantIndex The participant index for age evaluation * - * @return array Filtered array of valid course selections + * @return list Filtered array of valid course selections */ private function filterValidServiceSelections( array $selectedServices, @@ -145,10 +145,10 @@ class ParticipantCoursesFieldHandler extends AbstractParticipantFieldHandler /** * Validates if a selected course is still valid for the participant. * - * @param mixed $selectedService The selected course to validate - * @param array $availableServices Array of available courses - * @param BookingDto $bookingDto The booking DTO for context - * @param int $participantIndex The participant index for age evaluation + * @param mixed $selectedService The selected course to validate + * @param array $availableServices Array of available courses + * @param BookingDto $bookingDto The booking DTO for context + * @param int $participantIndex The participant index for age evaluation * * @return bool True if the course is valid for the participant, false otherwise */ diff --git a/src/Form/Service/ParticipantDateOfBirthFieldHandler.php b/src/Form/Service/ParticipantDateOfBirthFieldHandler.php index 32ea507..bcff63b 100644 --- a/src/Form/Service/ParticipantDateOfBirthFieldHandler.php +++ b/src/Form/Service/ParticipantDateOfBirthFieldHandler.php @@ -100,9 +100,9 @@ class ParticipantDateOfBirthFieldHandler extends AbstractParticipantFieldHandler if (true === is_array($value)) { // Check if all required fields are present and valid if (false === isset($value['year'], $value['month'], $value['day']) - || '' === trim((string) ($value['year'] ?? '')) - || '' === trim((string) ($value['month'] ?? '')) - || '' === trim((string) ($value['day'] ?? '')) + || '' === trim((string) $value['year']) + || '' === trim((string) $value['month']) + || '' === trim((string) $value['day']) ) { return null; } diff --git a/src/Form/Service/ParticipantFieldOptionsProvider.php b/src/Form/Service/ParticipantFieldOptionsProvider.php index 2a3f557..91561a7 100644 --- a/src/Form/Service/ParticipantFieldOptionsProvider.php +++ b/src/Form/Service/ParticipantFieldOptionsProvider.php @@ -511,7 +511,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider return [ 'label' => 'Hinfahrt', 'choices' => $choices, - 'choice_label' => fn (Service $service) => $service?->label, + 'choice_label' => fn (Service $service) => $service->label, 'choice_value' => 'id', 'expanded' => true, 'multiple' => false, @@ -551,7 +551,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider return [ 'label' => 'Rückfahrt', 'choices' => $choices, - 'choice_label' => fn (Service $service) => $service?->label, + 'choice_label' => fn (Service $service) => $service->label, 'choice_value' => 'id', 'expanded' => true, 'multiple' => false, @@ -622,7 +622,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider $hasOutboundBus = null !== $participant?->transportationOutbound && DirectionMapper::SUBTYPE_BUS_API === $participant->transportationOutbound->subType; - if ($hasOutboundBus && false === ($participant->differentDropOff ?? false)) { + if ($hasOutboundBus && false === $participant->differentDropOff) { return []; } @@ -806,11 +806,11 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider * as the participant's selected skipass. This ensures rental equipment * is only available for the exact duration of the skipass. * - * @param array $rentals Array of rental Service objects to filter - * @param BookingDto $bookingDto The booking DTO containing participant data - * @param int $participantIndex Index of the participant to evaluate + * @param array $rentals Array of rental Service objects to filter + * @param BookingDto $bookingDto The booking DTO containing participant data + * @param int $participantIndex Index of the participant to evaluate * - * @return array Filtered array of rentals matching skipass duration + * @return array Filtered array of rentals matching skipass duration */ private function filterRentalsBySkiPassDuration(array $rentals, BookingDto $bookingDto, int $participantIndex): array { @@ -914,8 +914,8 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider /** * Checks if a service array contains a service with the given ID. * - * @param array $services Array of Service objects - * @param int $serviceId Service ID to search for + * @param array $services Array of Service objects + * @param int $serviceId Service ID to search for * * @return bool True if the service is found in the array */ @@ -937,11 +937,11 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider * If no age evaluator is configured or participant has no birth date, * returns empty array to be handled by field visibility conditions. * - * @param array $services Array of Service objects to filter - * @param BookingDto $bookingDto The booking DTO containing participant data - * @param int $participantIndex Index of the participant to evaluate + * @param array $services Array of Service objects to filter + * @param BookingDto $bookingDto The booking DTO containing participant data + * @param int $participantIndex Index of the participant to evaluate * - * @return array Filtered array of available services + * @return array Filtered array of available services */ private function filterServicesByAgeConstraints(array $services, BookingDto $bookingDto, int $participantIndex): array { @@ -1112,6 +1112,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider /** * Gets the rental insurance description for help text. */ + /** @param list $rentalInsuranceManagers */ private function getRentalInsuranceDescription(array $rentalInsuranceManagers): ?string { if (empty($rentalInsuranceManagers)) { @@ -1131,7 +1132,7 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider * @param BookingDto $bookingDto The booking DTO containing travel and participant data * @param int $participantIndex The index of the participant to get eligible insurances for * - * @return array Array of eligible insurance objects filtered by age, family status, and other constraints + * @return list Array of eligible insurance objects filtered by age, family status, and other constraints */ private function getEligibleInsurances(BookingDto $bookingDto, int $participantIndex): array { @@ -1180,11 +1181,11 @@ class ParticipantFieldOptionsProvider extends AbstractFieldOptionsProvider * got the discount to change their mind (e.g., select bus instead), making the discount available * for others in the same booking session. * - * @param array $services Transportation services from Travel model - * @param BookingDto $bookingDto Current booking DTO with participant selections - * @param int $participantIndex Current participant being processed + * @param array $services Transportation services from Travel model + * @param BookingDto $bookingDto Current booking DTO with participant selections + * @param int $participantIndex Current participant being processed * - * @return array Filtered transportation choices with smart PKW option selection + * @return array Filtered transportation choices with smart PKW option selection */ private function filterTransportationChoices(array $services, BookingDto $bookingDto, int $participantIndex): array { diff --git a/src/Form/Service/ParticipantRentalsFieldHandler.php b/src/Form/Service/ParticipantRentalsFieldHandler.php index a0c28ca..25b37d5 100644 --- a/src/Form/Service/ParticipantRentalsFieldHandler.php +++ b/src/Form/Service/ParticipantRentalsFieldHandler.php @@ -128,6 +128,10 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler $participant->rentals = $validSelections; } + /** + * @param array $availableRentals + * @param list $validSelections + */ private function updateAutoBookOptOutRentals( ParticipantDto $participant, array $availableRentals, @@ -137,10 +141,6 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler ): void { $currentlySelectedAutoBookIds = []; foreach ($participant->rentals as $selectedService) { - if (false === $selectedService instanceof Service) { - continue; - } - if (null === $selectedService->id || true === $selectedService->mandatory || false === $selectedService->autoBook) { continue; } @@ -174,6 +174,12 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler )); } + /** + * @param list $selectedServices + * @param array $availableServices + * + * @return list + */ private function filterValidServiceSelections( array $selectedServices, array $availableServices, @@ -195,6 +201,9 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler return $validSelections; } + /** + * @param array $availableServices + */ private function isServiceValidForParticipant( mixed $selectedService, array $availableServices, @@ -222,12 +231,12 @@ class ParticipantRentalsFieldHandler extends AbstractParticipantFieldHandler * - rental.dateFrom === skipass.dateFrom * - rental.dateTo === skipass.dateTo * - * @param array $rentals All available rental services - * @param ParticipantDto $participant The participant with skipass selection + * @param array $rentals All available rental services + * @param ParticipantDto $participant The participant with skipass selection * - * @return array Filtered rentals matching the skipass duration + * @return array Filtered rentals matching the skipass duration */ - private function filterRentalsBySkiPassDuration(array $rentals, $participant): array + private function filterRentalsBySkiPassDuration(array $rentals, ParticipantDto $participant): array { $selectedSkiPass = $participant->skiPass; diff --git a/src/Form/Service/ParticipantSkiPassFieldHandler.php b/src/Form/Service/ParticipantSkiPassFieldHandler.php index ec8bf7a..b28248e 100644 --- a/src/Form/Service/ParticipantSkiPassFieldHandler.php +++ b/src/Form/Service/ParticipantSkiPassFieldHandler.php @@ -129,6 +129,7 @@ class ParticipantSkiPassFieldHandler extends AbstractParticipantFieldHandler $participant->skiPass = $validSelection; } + /** @param array $availableSkipasses */ private function getSelectedAutoBookSkiPassId( ParticipantDto $participant, array $availableSkipasses, @@ -184,10 +185,10 @@ class ParticipantSkiPassFieldHandler extends AbstractParticipantFieldHandler * This method checks both age constraints (via birth year ranges) and * date constraints (skipass dates must be within travel dates). * - * @param mixed $selectedService The selected skipass to validate - * @param array $availableServices Array of available skipasses - * @param BookingDto $bookingDto The booking DTO for context - * @param int $participantIndex The participant index for age evaluation + * @param mixed $selectedService The selected skipass to validate + * @param array $availableServices Array of available skipasses + * @param BookingDto $bookingDto The booking DTO for context + * @param int $participantIndex The participant index for age evaluation * * @return bool True if the skipass is valid for the participant, false otherwise */ diff --git a/src/Form/Service/ParticipantVegFieldHandler.php b/src/Form/Service/ParticipantVegFieldHandler.php index ec30873..73355d6 100644 --- a/src/Form/Service/ParticipantVegFieldHandler.php +++ b/src/Form/Service/ParticipantVegFieldHandler.php @@ -107,10 +107,10 @@ class ParticipantVegFieldHandler extends AbstractParticipantFieldHandler * * This method checks age constraints if they exist for the service. * - * @param mixed $selectedService The selected veg option to validate - * @param array $availableServices Array of available veg options - * @param BookingDto $bookingDto The booking DTO for context - * @param int $participantIndex The participant index for age evaluation + * @param mixed $selectedService The selected veg option to validate + * @param array $availableServices Array of available veg options + * @param BookingDto $bookingDto The booking DTO for context + * @param int $participantIndex The participant index for age evaluation * * @return bool True if the option is valid for the participant, false otherwise */ diff --git a/src/Form/Service/Trait/FormTraversalTrait.php b/src/Form/Service/Trait/FormTraversalTrait.php index fb332a0..87d14a6 100644 --- a/src/Form/Service/Trait/FormTraversalTrait.php +++ b/src/Form/Service/Trait/FormTraversalTrait.php @@ -26,7 +26,7 @@ trait FormTraversalTrait * and extracts the BookingDto data. This is used as a fallback when * BookingDto is not passed explicitly via form options. * - * @param FormInterface $form The form to start traversing from + * @param FormInterface $form The form to start traversing from * * @return BookingDto|null The booking DTO or null if not found */ diff --git a/src/Form/StepSelectChoiceType.php b/src/Form/StepSelectChoiceType.php index f38e4a3..845a6c6 100644 --- a/src/Form/StepSelectChoiceType.php +++ b/src/Form/StepSelectChoiceType.php @@ -8,6 +8,7 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; +/** @extends AbstractType */ class StepSelectChoiceType extends AbstractType { public function getParent(): string diff --git a/src/Service/BookingChangeTracker.php b/src/Service/BookingChangeTracker.php index f75c520..a59b403 100644 --- a/src/Service/BookingChangeTracker.php +++ b/src/Service/BookingChangeTracker.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Service; +use App\BusProNet\Model\Service; use App\Form\Model\BookingDto; use App\Form\Model\ParticipantDto; @@ -36,7 +37,7 @@ class BookingChangeTracker * * @param BookingDto $bookingDto The booking DTO to extract data from * - * @return array Serializable array of all user-editable data + * @return array Serializable array of all user-editable data */ public function extractUserData(BookingDto $bookingDto): array { @@ -61,7 +62,7 @@ class BookingChangeTracker * * @param ParticipantDto $participant The participant to extract data from * - * @return array Serializable array of participant data + * @return array Serializable array of participant data */ private function extractParticipantData(ParticipantDto $participant): array { @@ -120,9 +121,9 @@ class BookingChangeTracker * This ensures that associative arrays, indexed arrays, and different orders * all produce the same fingerprint as long as the same services are present. * - * @param array $services Array of Service objects + * @param list $services Array of Service objects * - * @return array Sorted array of service IDs + * @return list Sorted array of service IDs */ private function normalizeServiceArray(array $services): array { @@ -130,7 +131,7 @@ class BookingChangeTracker $ids = array_unique($ids); sort($ids); - return array_values($ids); + return $ids; } /** diff --git a/src/Service/BookingConfigurator.php b/src/Service/BookingConfigurator.php index f852255..41b36bf 100644 --- a/src/Service/BookingConfigurator.php +++ b/src/Service/BookingConfigurator.php @@ -95,8 +95,8 @@ class BookingConfigurator * Converts a Room model into a RoomSelectionDto with the specified quantity * selection. Used during booking initialization to create selectable room options. * - * @param Room $room The room model to convert - * @param array $roomsIdsAndQuantities Array of room ID to quantity mappings + * @param Room $room The room model to convert + * @param array $roomsIdsAndQuantities Array of room ID to quantity mappings * * @return RoomSelectionDto The room selection DTO */ @@ -249,6 +249,7 @@ class BookingConfigurator return $this->participantEligibilityService->isParticipantEligible($bookingDto, $participantIndex); } + /** @param array $mandatoryAdditionalServices */ private function preselectMandatoryAdditionalServices( ParticipantDto $participant, array $mandatoryAdditionalServices, @@ -266,6 +267,7 @@ class BookingConfigurator $this->appendAdditionalServices($participant, $eligibleServices, false); } + /** @param array $autoBookAdditionalServices */ private function preselectAutoBookAdditionalServices( ParticipantDto $participant, array $autoBookAdditionalServices, @@ -283,6 +285,7 @@ class BookingConfigurator $this->appendAdditionalServices($participant, $eligibleServices, true); } + /** @param array $mandatorySkiPassServices */ private function preselectMandatorySkiPass( ParticipantDto $participant, array $mandatorySkiPassServices, @@ -308,6 +311,7 @@ class BookingConfigurator } } + /** @param array $autoBookSkiPassServices */ private function preselectAutoBookSkiPass( ParticipantDto $participant, array $autoBookSkiPassServices, @@ -338,6 +342,7 @@ class BookingConfigurator } } + /** @param array $mandatoryBoardServices */ private function preselectMandatoryBoardServices( ParticipantDto $participant, array $mandatoryBoardServices, @@ -355,6 +360,7 @@ class BookingConfigurator $this->appendBoardServices($participant, $eligibleServices, false); } + /** @param array $autoBookBoardServices */ private function preselectAutoBookBoardServices( ParticipantDto $participant, array $autoBookBoardServices, @@ -372,6 +378,7 @@ class BookingConfigurator $this->appendBoardServices($participant, $eligibleServices, true); } + /** @param array $mandatoryRentalServices */ private function preselectMandatoryRentals( ParticipantDto $participant, array $mandatoryRentalServices, @@ -395,6 +402,7 @@ class BookingConfigurator $this->appendRentalServices($participant, $matchingDurationServices, false); } + /** @param array $autoBookRentalServices */ private function preselectAutoBookRentals( ParticipantDto $participant, array $autoBookRentalServices, @@ -445,7 +453,7 @@ class BookingConfigurator */ private function appendAdditionalServices(ParticipantDto $participant, array $services, bool $respectOptOut): void { - $currentSelections = $participant->additionalServices ?? []; + $currentSelections = $participant->additionalServices; $currentServiceIds = array_map(static fn (Service $service): ?int => $service->id, $currentSelections); foreach ($services as $service) { @@ -469,7 +477,7 @@ class BookingConfigurator */ private function appendBoardServices(ParticipantDto $participant, array $services, bool $respectOptOut): void { - $currentSelections = $participant->board ?? []; + $currentSelections = $participant->board; $currentServiceIds = array_map(static fn (Service $service): ?int => $service->id, $currentSelections); foreach ($services as $service) { @@ -493,7 +501,7 @@ class BookingConfigurator */ private function appendRentalServices(ParticipantDto $participant, array $services, bool $respectOptOut): void { - $currentSelections = $participant->rentals ?? []; + $currentSelections = $participant->rentals; $currentServiceIds = array_map(static fn (Service $service): ?int => $service->id, $currentSelections); foreach ($services as $service) { diff --git a/src/Service/BookingEditDraftManager.php b/src/Service/BookingEditDraftManager.php index 39dc0be..abbf87a 100644 --- a/src/Service/BookingEditDraftManager.php +++ b/src/Service/BookingEditDraftManager.php @@ -178,6 +178,7 @@ class BookingEditDraftManager /** * Applies bank account data from draft to BookingDto. */ + /** @param array $bankAccountData */ private function applyBankAccountData(BookingDto $dto, array $bankAccountData): void { $iban = $bankAccountData['iban'] ?? null; diff --git a/src/Service/BookingEditDraftMerger.php b/src/Service/BookingEditDraftMerger.php index 8ec05d1..1ad55e7 100644 --- a/src/Service/BookingEditDraftMerger.php +++ b/src/Service/BookingEditDraftMerger.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace App\Service; +use App\BusProNet\Model\Service; use App\BusProNet\Model\Travel; use App\Form\Model\BookingDto; use App\Form\Model\ParticipantDto; @@ -21,6 +22,7 @@ use App\Form\Model\ParticipantDto; */ class BookingEditDraftMerger { + /** @param array $data */ public function apply( BookingDto $bookingDto, int $participantIndex, @@ -79,6 +81,7 @@ class BookingEditDraftMerger return $participant->mutable; } + /** @param array $data */ private function applyPersonalData(ParticipantDto $participant, array $data): void { if (true === array_key_exists('firstName', $data)) { @@ -104,6 +107,7 @@ class BookingEditDraftMerger } } + /** @param array $data */ private function applyAddressData(ParticipantDto $participant, array $data): void { $hasAddressData = null !== ($data['street'] ?? null) @@ -133,6 +137,7 @@ class BookingEditDraftMerger } } + /** @param array $data */ private function applyBodyDimensions(ParticipantDto $participant, array $data): void { if (true === array_key_exists('height', $data)) { @@ -146,6 +151,7 @@ class BookingEditDraftMerger } } + /** @param array $data */ private function applyRoomAssignment(ParticipantDto $participant, array $data): void { if (true === array_key_exists('assignedRoomId', $data)) { @@ -156,6 +162,7 @@ class BookingEditDraftMerger } } + /** @param array $data */ private function applyVoucherCodes(ParticipantDto $participant, array $data): void { if (true === array_key_exists('purchaseVoucherCode', $data)) { @@ -177,6 +184,7 @@ class BookingEditDraftMerger * Multi-select fields (checkboxes) and booleans use overwrite strategy: draft values * always replace API data, since users can intentionally clear these selections. */ + /** @param array $data */ private function applyServiceSelections(ParticipantDto $participant, array $data, Travel $travel): void { // Additional services category — only apply draft data when services are mutable. @@ -296,11 +304,11 @@ class BookingEditDraftMerger * The mandatory flag must be looked up from travel data since booking data * doesn't include the pflicht attribute. * - * @param array $draftServices Services resolved from draft data - * @param array $originalServices Services from fresh API data (booking assignments) - * @param Travel $travel Travel data containing mandatory flag on services + * @param list $draftServices Services resolved from draft data + * @param list $originalServices Services from fresh API data (booking assignments) + * @param Travel $travel Travel data containing mandatory flag on services * - * @return array Merged array with draft services plus any missing mandatory services + * @return list Merged array with draft services plus any missing mandatory services */ private function preserveMandatoryServices(array $draftServices, array $originalServices, Travel $travel): array { diff --git a/src/Service/BookingEditSubmitGuard.php b/src/Service/BookingEditSubmitGuard.php index bbe79a3..8fb0786 100644 --- a/src/Service/BookingEditSubmitGuard.php +++ b/src/Service/BookingEditSubmitGuard.php @@ -171,6 +171,10 @@ class BookingEditSubmitGuard return $changed; } + /** + * @param list $left + * @param list $right + */ private function areServiceListsEqual(array $left, array $right): bool { return $this->normalizeServiceIds($left) === $this->normalizeServiceIds($right); diff --git a/src/Service/BookingEditSubmitter.php b/src/Service/BookingEditSubmitter.php index e2489b2..c486319 100644 --- a/src/Service/BookingEditSubmitter.php +++ b/src/Service/BookingEditSubmitter.php @@ -13,6 +13,7 @@ use App\Form\Model\BookingDto; use Psr\Log\LoggerInterface; use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Session\FlashBagAwareSessionInterface; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; /** @@ -119,7 +120,10 @@ class BookingEditSubmitter private function addFlash(Request $request, string $type, string $message): void { - $request->getSession()->getFlashBag()->add($type, $message); + $session = $request->getSession(); + if ($session instanceof FlashBagAwareSessionInterface) { + $session->getFlashBag()->add($type, $message); + } } private function redirectToEdit(int $bookingId): RedirectResponse diff --git a/src/Service/BookingExporter.php b/src/Service/BookingExporter.php index e02f1b0..76040f2 100644 --- a/src/Service/BookingExporter.php +++ b/src/Service/BookingExporter.php @@ -156,8 +156,8 @@ class BookingExporter /** * Creates the spreadsheet with participant data. * - * @param BookingEditDraft $draft The draft containing form data - * @param array $lookups The ID to label lookup arrays + * @param BookingEditDraft $draft The draft containing form data + * @param array> $lookups The ID to label lookup arrays */ private function createSpreadsheet(BookingEditDraft $draft, array $lookups): Spreadsheet { @@ -203,6 +203,10 @@ class BookingExporter /** * Writes a single participant row to the spreadsheet. */ + /** + * @param array $participant + * @param array> $lookups + */ private function writeParticipantRow( \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet $sheet, int $row, @@ -298,6 +302,7 @@ class BookingExporter /** * Resolves a single service ID to its label. */ + /** @param array $lookup */ private function resolveService(int|string|null $serviceId, array $lookup): string { if (null === $serviceId || '' === $serviceId) { @@ -312,6 +317,10 @@ class BookingExporter /** * Resolves an array of service IDs to labels, joined by pipe. */ + /** + * @param list $serviceIds + * @param array $lookup + */ private function resolveServiceArray(array $serviceIds, array $lookup): string { if (true === empty($serviceIds)) { @@ -332,6 +341,7 @@ class BookingExporter /** * Resolves a room ID to its label. */ + /** @param array $lookup */ private function resolveRoom(?int $roomId, array $lookup): string { if (null === $roomId) { @@ -344,6 +354,7 @@ class BookingExporter /** * Resolves a pickup ID to its label. */ + /** @param array $lookup */ private function resolvePickup(?int $pickupId, array $lookup): string { if (null === $pickupId) { diff --git a/src/Service/BookingPriceMismatchAnalyzer.php b/src/Service/BookingPriceMismatchAnalyzer.php index 8003c54..960245f 100644 --- a/src/Service/BookingPriceMismatchAnalyzer.php +++ b/src/Service/BookingPriceMismatchAnalyzer.php @@ -26,8 +26,8 @@ class BookingPriceMismatchAnalyzer { $pricingBreakdown = $this->pricingAssembler->getPricingBreakdown($bookingCreateDto); - $roomLines = $pricingBreakdown['rooms'] ?? []; - $serviceGroups = $pricingBreakdown['services'] ?? []; + $roomLines = $pricingBreakdown['rooms']; + $serviceGroups = $pricingBreakdown['services']; $roomTotal = round(array_sum(array_column($roomLines, 'totalPrice')), 2); $serviceTotal = round(array_sum(array_column($serviceGroups, 'groupTotal')), 2); @@ -70,14 +70,14 @@ class BookingPriceMismatchAnalyzer $apiTotal = round($response->totalPrice ?? 0.0, 2); $apiServiceTotal = round($apiTotal - $apiRoomTotal, 2); - $deltaTotal = round(($pricingBreakdown['grandTotal'] ?? 0.0) - $apiTotal, 2); + $deltaTotal = round($pricingBreakdown['grandTotal'] - $apiTotal, 2); $deltaRoom = round($roomTotal - $apiRoomTotal, 2); $deltaService = round($serviceTotal - $apiServiceTotal, 2); $deltaInsurance = round($insuranceTotal - $apiInsuranceTotal, 2); return [ 'localBreakdown' => [ - 'grandTotal' => round($pricingBreakdown['grandTotal'] ?? 0.0, 2), + 'grandTotal' => round($pricingBreakdown['grandTotal'], 2), 'roomTotal' => $roomTotal, 'serviceTotal' => $serviceTotal, 'insuranceTotal' => $insuranceTotal, diff --git a/src/Service/BookingPricingAssembler.php b/src/Service/BookingPricingAssembler.php index 5417923..fa59fc3 100644 --- a/src/Service/BookingPricingAssembler.php +++ b/src/Service/BookingPricingAssembler.php @@ -31,7 +31,7 @@ class BookingPricingAssembler /** * Assembles a complete pricing breakdown for display or diagnostic use. * - * @return array{rooms: array, services: array, grandTotal: float, surcharges?: array} + * @return array{rooms: array>, services: array>, grandTotal: float, surcharges?: array>} */ public function getPricingBreakdown( BookingDto $bookingDto, @@ -251,7 +251,8 @@ class BookingPricingAssembler foreach ($serviceAggregation as $serviceData) { $subType = $serviceData['subType'] ?? 'other'; - if (null === $subType || '' === $subType) { + + if ('' === $subType) { $subType = 'other'; } @@ -308,6 +309,7 @@ class BookingPricingAssembler return Constants::SERVICE_LABELS[$subType] ?? 'Sonstige Leistungen'; } + /** @param array> $serviceAggregation */ private function aggregateParticipantServices( ParticipantDto $participant, array &$serviceAggregation, @@ -338,16 +340,15 @@ class BookingPricingAssembler ]; foreach ($multipleServiceArrays as $serviceArray) { - if (true === is_array($serviceArray)) { - foreach ($serviceArray as $service) { - if ($service instanceof Service && null !== $service->price) { - $this->addToServiceAggregation($serviceAggregation, $service); - } + foreach ($serviceArray as $service) { + if (null !== $service->price) { + $this->addToServiceAggregation($serviceAggregation, $service); } } } } + /** @param array> $serviceAggregation */ private function addToServiceAggregation(array &$serviceAggregation, Service $service, int $quantity = 1): void { $serviceKey = $service->id.'_'.$service->label; @@ -367,6 +368,7 @@ class BookingPricingAssembler $serviceAggregation[$serviceKey]['totalPrice'] += $service->price * $quantity; } + /** @param array> $serviceAggregation */ private function addInsuranceToServiceAggregation(array &$serviceAggregation, Insurance $insurance, int $quantity = 1): void { $serviceKey = $insurance->id.'_'.$insurance->label; diff --git a/src/Service/BookingSessionManager.php b/src/Service/BookingSessionManager.php index 79046dd..d93d1dd 100644 --- a/src/Service/BookingSessionManager.php +++ b/src/Service/BookingSessionManager.php @@ -5,6 +5,8 @@ declare(strict_types=1); namespace App\Service; use App\Exception\BookingSessionNotFoundException; +use App\Exception\HotelNotInTravelException; +use App\Exception\TravelNotFoundException; use App\Form\Model\BookingDto; use Symfony\Component\HttpFoundation\Request; @@ -32,6 +34,8 @@ class BookingSessionManager * Retrieves the booking creation DTO from the session. * * @throws BookingSessionNotFoundException + * @throws TravelNotFoundException + * @throws HotelNotInTravelException */ public function getOrCreateBookingCreateDto(Request $request): BookingDto { @@ -46,6 +50,8 @@ class BookingSessionManager /** * Gets or creates the baseline room selection snapshot for change detection. + * + * @return array */ public function getOrCreateBaselineSnapshot(Request $request, BookingDto $bookingCreateDto): array { diff --git a/src/Service/CmsDataProvider.php b/src/Service/CmsDataProvider.php index 2a211dc..1d385eb 100644 --- a/src/Service/CmsDataProvider.php +++ b/src/Service/CmsDataProvider.php @@ -27,6 +27,7 @@ class CmsDataProvider return $result['hotel']['images'] ?? null; } + /** @return array */ public function getProductDetails(string $productCode, ?string $hotelCode = null): array { try { diff --git a/src/Service/InsuranceManager.php b/src/Service/InsuranceManager.php index d744cf6..1ce2042 100644 --- a/src/Service/InsuranceManager.php +++ b/src/Service/InsuranceManager.php @@ -43,7 +43,7 @@ class InsuranceManager { $cacheKey = 'selectable_insurances_'.$travel->id; - return $this->selectableInsurancesCache[$cacheKey] ??= $this->filterNonComplementary($travel->insurances ?? []); + return $this->selectableInsurancesCache[$cacheKey] ??= $this->filterNonComplementary($travel->insurances); } /** diff --git a/src/Service/NewsletterManager.php b/src/Service/NewsletterManager.php index 6a25e4d..949015c 100644 --- a/src/Service/NewsletterManager.php +++ b/src/Service/NewsletterManager.php @@ -69,7 +69,7 @@ class NewsletterManager ]; $this->mailer->createAndSendEmail($context, $options); } catch (\Throwable $exception) { - if (true === $wasExisting && null !== $previousTokenHash && null !== $previousExpiresAt) { + if (true === $wasExisting) { $pendingConfirmation->refreshRequest($previousTokenHash, $previousExpiresAt); } else { $this->entityManager->remove($pendingConfirmation); diff --git a/src/Service/ParticipantPricingCalculator.php b/src/Service/ParticipantPricingCalculator.php index 96b38c3..efc481a 100644 --- a/src/Service/ParticipantPricingCalculator.php +++ b/src/Service/ParticipantPricingCalculator.php @@ -65,7 +65,7 @@ class ParticipantPricingCalculator * * @param BookingDto $bookingDto The booking data containing all participants * - * @return array Array indexed by participant index containing individual prices + * @return array Array indexed by participant index containing individual prices */ public function calculateAllParticipantIndividualPrices(BookingDto $bookingDto): array { @@ -106,17 +106,17 @@ class ParticipantPricingCalculator // Generate cache key based on participant state that affects pricing $stateComponents = [ 'room' => $participant->assignedRoomId ?? 'none', - 'skiPass' => $participant->skiPass?->id ?? 'none', - 'rentalInsurance' => $participant->rentalInsurance?->id ?? 'none', - 'transportationOut' => $participant->transportationOutbound?->id ?? 'none', - 'transportationIn' => $participant->transportationInbound?->id ?? 'none', - 'pickup' => $participant->pickup?->id ?? 'none', - 'dropOff' => $participant->dropOff?->id ?? 'none', - 'parking' => $participant->parkingService?->id ?? 'none', - 'courses' => implode('_', array_map(fn ($s) => $s->id, $participant->courses ?? [])), - 'additionalServices' => implode('_', array_map(fn ($s) => $s->id, $participant->additionalServices ?? [])), - 'board' => implode('_', array_map(fn ($s) => $s->id, $participant->board ?? [])), - 'rentals' => implode('_', array_map(fn ($s) => $s->id, $participant->rentals ?? [])), + 'skiPass' => $participant->skiPass->id ?? 'none', + 'rentalInsurance' => $participant->rentalInsurance->id ?? 'none', + 'transportationOut' => $participant->transportationOutbound->id ?? 'none', + 'transportationIn' => $participant->transportationInbound->id ?? 'none', + 'pickup' => $participant->pickup->id ?? 'none', + 'dropOff' => $participant->dropOff->id ?? 'none', + 'parking' => $participant->parkingService->id ?? 'none', + 'courses' => implode('_', array_map(fn ($s) => $s->id, $participant->courses)), + 'additionalServices' => implode('_', array_map(fn ($s) => $s->id, $participant->additionalServices)), + 'board' => implode('_', array_map(fn ($s) => $s->id, $participant->board)), + 'rentals' => implode('_', array_map(fn ($s) => $s->id, $participant->rentals)), ]; $cacheKey = sprintf( @@ -261,12 +261,10 @@ class ParticipantPricingCalculator ]; foreach ($multipleServiceArrays as $serviceArray) { - if (true === is_array($serviceArray)) { - foreach ($serviceArray as $service) { - if ($service instanceof Service && null !== $service->price) { - if (false === $onlyInsuranceCalculationServices || true === $service->includeInInsuranceCalculation) { - $serviceTotal += $service->price; - } + foreach ($serviceArray as $service) { + if (null !== $service->price) { + if (false === $onlyInsuranceCalculationServices || true === $service->includeInInsuranceCalculation) { + $serviceTotal += $service->price; } } } diff --git a/src/Service/RoomPricingCalculator.php b/src/Service/RoomPricingCalculator.php index 785ef62..79bb4df 100644 --- a/src/Service/RoomPricingCalculator.php +++ b/src/Service/RoomPricingCalculator.php @@ -23,7 +23,7 @@ class RoomPricingCalculator * * @param BookingDto $bookingDto The booking data containing room selections * - * @return array Array of room pricing data with labels, quantities, and totals + * @return array> Array of room pricing data with labels, quantities, and totals */ public function calculateRoomPricing( BookingDto $bookingDto, @@ -73,7 +73,7 @@ class RoomPricingCalculator * * @param BookingDto $bookingDto The booking data containing room selections * - * @return array Array of room pricing data + * @return array> Array of room pricing data */ private function calculateRoomPricingFromSelections(BookingDto $bookingDto, string $pricingMode): array { @@ -122,7 +122,7 @@ class RoomPricingCalculator * * @param BookingDto $bookingDto The booking data with booking entity * - * @return array Array of room pricing data with labels, quantities, and totals + * @return array> Array of room pricing data with labels, quantities, and totals */ private function calculateRoomPricingFromBooking(BookingDto $bookingDto): array { diff --git a/src/Service/ServiceAvailabilityCalculator.php b/src/Service/ServiceAvailabilityCalculator.php index c60a9db..1d8b931 100644 --- a/src/Service/ServiceAvailabilityCalculator.php +++ b/src/Service/ServiceAvailabilityCalculator.php @@ -8,6 +8,7 @@ use App\BusProNet\Constants; use App\BusProNet\Model\Service; use App\BusProNet\Utility\DirectionMapper; use App\Form\Model\BookingDto; +use App\Form\Model\ParticipantDto; /** * Calculates dynamic service availability based on current booking selections. @@ -52,11 +53,11 @@ class ServiceAvailabilityCalculator /** * Filter services array to only include those with remaining availability. * - * @param array $services Array of Service objects to filter - * @param BookingDto $bookingDto The booking data with participant selections - * @param int $participantIndex The index of the participant currently filling the form + * @param array $services Array of Service objects to filter + * @param BookingDto $bookingDto The booking data with participant selections + * @param int $participantIndex The index of the participant currently filling the form * - * @return array Filtered array containing only available services + * @return array Filtered array containing only available services */ public function filterAvailableServices(array $services, BookingDto $bookingDto, int $participantIndex): array { @@ -139,7 +140,11 @@ class ServiceAvailabilityCalculator } // Count service selections for this participant - $this->countParticipantServiceUsage($participant, $serviceUsage); + $participantUsage = $this->countParticipantServiceUsage($participant); + + foreach ($participantUsage as $serviceId => $count) { + $serviceUsage[$serviceId] = ($serviceUsage[$serviceId] ?? 0) + $count; + } } return $serviceUsage; @@ -148,56 +153,57 @@ class ServiceAvailabilityCalculator /** * Count service usage for a single participant and add to the usage array. * - * @param mixed $participant The participant DTO object - * @param array $serviceUsage Reference to the service usage array to update + * @param ParticipantDto $participant The participant DTO object + * + * @return array Usage counts keyed by service ID */ - private function countParticipantServiceUsage($participant, array &$serviceUsage): void + private function countParticipantServiceUsage(ParticipantDto $participant): array { + $serviceUsage = []; + // Board service (single selection) - if (isset($participant->board) && $participant->board instanceof Service) { - $serviceUsage[$participant->board->id] = ($serviceUsage[$participant->board->id] ?? 0) + 1; + foreach ($participant->board as $service) { + if (null !== $service->id) { + $serviceUsage[$service->id] = ($serviceUsage[$service->id] ?? 0) + 1; + } } // Ski pass service (single selection) - if (isset($participant->skiPass) && $participant->skiPass instanceof Service) { + if (null !== $participant->skiPass && null !== $participant->skiPass->id) { $serviceUsage[$participant->skiPass->id] = ($serviceUsage[$participant->skiPass->id] ?? 0) + 1; } // Transportation services (single selection each) - if (isset($participant->transportationOutbound) && $participant->transportationOutbound instanceof Service) { + if (null !== $participant->transportationOutbound && null !== $participant->transportationOutbound->id) { $serviceUsage[$participant->transportationOutbound->id] = ($serviceUsage[$participant->transportationOutbound->id] ?? 0) + 1; } - if (isset($participant->transportationInbound) && $participant->transportationInbound instanceof Service) { + if (null !== $participant->transportationInbound && null !== $participant->transportationInbound->id) { $serviceUsage[$participant->transportationInbound->id] = ($serviceUsage[$participant->transportationInbound->id] ?? 0) + 1; } // Courses (multiple selection) - if (isset($participant->courses) && is_array($participant->courses)) { - foreach ($participant->courses as $course) { - if ($course instanceof Service) { - $serviceUsage[$course->id] = ($serviceUsage[$course->id] ?? 0) + 1; - } + foreach ($participant->courses as $course) { + if (null !== $course->id) { + $serviceUsage[$course->id] = ($serviceUsage[$course->id] ?? 0) + 1; } } // Additional services (multiple selection) - if (isset($participant->additionalServices) && is_array($participant->additionalServices)) { - foreach ($participant->additionalServices as $additionalService) { - if ($additionalService instanceof Service) { - $serviceUsage[$additionalService->id] = ($serviceUsage[$additionalService->id] ?? 0) + 1; - } + foreach ($participant->additionalServices as $additionalService) { + if (null !== $additionalService->id) { + $serviceUsage[$additionalService->id] = ($serviceUsage[$additionalService->id] ?? 0) + 1; } } // Rentals (multiple selection) - if (isset($participant->rentals) && is_array($participant->rentals)) { - foreach ($participant->rentals as $rental) { - if ($rental instanceof Service) { - $serviceUsage[$rental->id] = ($serviceUsage[$rental->id] ?? 0) + 1; - } + foreach ($participant->rentals as $rental) { + if (null !== $rental->id) { + $serviceUsage[$rental->id] = ($serviceUsage[$rental->id] ?? 0) + 1; } } + + return $serviceUsage; } /** @@ -213,8 +219,8 @@ class ServiceAvailabilityCalculator // Get all transportation services $transportationServices = array_merge( - $bookingDto->travel->getTransportationServicesByDirection(DirectionMapper::OUTBOUND_TRAVEL) ?? [], - $bookingDto->travel->getTransportationServicesByDirection(DirectionMapper::INBOUND_TRAVEL) ?? [] + $bookingDto->travel->getTransportationServicesByDirection(DirectionMapper::OUTBOUND_TRAVEL), + $bookingDto->travel->getTransportationServicesByDirection(DirectionMapper::INBOUND_TRAVEL) ); $allServices = array_merge($allServices, $transportationServices); @@ -228,7 +234,7 @@ class ServiceAvailabilityCalculator ]; foreach ($additionalServiceTokens as $token) { - $categoryServices = $bookingDto->travel->getAdditionalServicesBySubTypes($token) ?? []; + $categoryServices = $bookingDto->travel->getAdditionalServicesBySubTypes($token); $allServices = array_merge($allServices, $categoryServices); } diff --git a/src/Service/ServiceLabelFormatter.php b/src/Service/ServiceLabelFormatter.php index aa92a9e..fce54bc 100644 --- a/src/Service/ServiceLabelFormatter.php +++ b/src/Service/ServiceLabelFormatter.php @@ -40,10 +40,6 @@ final class ServiceLabelFormatter return $fallbackLabel; } - if (!$service instanceof Service) { - return $fallbackLabel; - } - return $this->formatServiceLabel($service); } } diff --git a/src/Service/TravelDataProvider.php b/src/Service/TravelDataProvider.php index 76f11a8..342c40e 100644 --- a/src/Service/TravelDataProvider.php +++ b/src/Service/TravelDataProvider.php @@ -11,7 +11,6 @@ use App\BusProNet\Model\Notification; use App\BusProNet\Model\ServiceAvailabilityResponse; use App\BusProNet\Model\Travel; use App\BusProNet\XmlLoader\TravelLoader; -use App\Exception\HotelNotFoundException; use App\Exception\HotelNotInTravelException; use App\Exception\TravelNotFoundException; use Psr\Cache\InvalidArgumentException; @@ -86,7 +85,6 @@ class TravelDataProvider * @return Travel|null The travel data or null on unexpected failure * * @throws TravelNotFoundException When the travel date is not found - * @throws HotelNotFoundException When the requested hotel is not found * @throws HotelNotInTravelException When the hotel does not belong to this travel */ public function getTravelDataFromXml(int $dateId, ?int $hotelId = null): ?Travel @@ -100,7 +98,7 @@ class TravelDataProvider 'error' => $e->getMessage(), ]); throw $e; - } catch (HotelNotFoundException|HotelNotInTravelException $e) { + } catch (HotelNotInTravelException $e) { $this->logger->debug('Hotel not found in XML', [ 'dateId' => $dateId, 'hotelId' => $hotelId, diff --git a/tests/Form/DataTransformer/RoomSelectionToIdTransformerTest.php b/tests/Form/DataTransformer/RoomSelectionToIdTransformerTest.php new file mode 100644 index 0000000..16bccec --- /dev/null +++ b/tests/Form/DataTransformer/RoomSelectionToIdTransformerTest.php @@ -0,0 +1,49 @@ +reverseTransform(12)); + self::assertSame(12, $transformer->reverseTransform('12')); + } + + public function testReverseTransformReturnsNullForEmptyValues(): void + { + $transformer = new RoomSelectionToIdTransformer([]); + + self::assertNull($transformer->reverseTransform(null)); + self::assertNull($transformer->reverseTransform('')); + } + + public function testReverseTransformRejectsInvalidValues(): void + { + $transformer = new RoomSelectionToIdTransformer([]); + + $this->expectException(TransformationFailedException::class); + + $transformer->reverseTransform('1.5'); + } + + public function testTransformReturnsMatchingRoomSelectionDto(): void + { + $roomSelection = new RoomSelectionDto(); + $roomSelection->id = 7; + $roomSelection->label = 'Doppelzimmer'; + + $transformer = new RoomSelectionToIdTransformer([$roomSelection]); + + self::assertSame($roomSelection, $transformer->transform(7)); + } +}