chore: major code cleanup and type safety using phpstan

This commit is contained in:
Björn Fromme committed 2026-04-16 17:11:16 +02:00
1 parent 060cbea9da
commit 3da73d626b
103 files changed
+976 -1192

No files matched your search

+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Admin\Field;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet;
@@ -128,7 +128,7 @@ class BookingDataProcessor
// Parking service (for self-organized PKW transportation)
$parkingServices = $booking->getAdditionalServicesForParticipantByGroup($index, Constants::TOKEN_PARKING);
if (false === empty($parkingServices)) {
if ([] !== $parkingServices) {
$participantData->parking = true;
$participantData->parkingService = reset($parkingServices);
} else {
@@ -320,7 +320,7 @@ class BookingDataProcessor
// Add purchase vouchers if any exist
$purchaseVouchers = $this->mappingCollector->collectPurchaseVouchers($formData);
if (false === empty($purchaseVouchers)) {
if ([] !== $purchaseVouchers) {
$payload['gutscheine']['gutschein'] = [];
foreach ($purchaseVouchers as $code) {
$payload['gutscheine']['gutschein'][] = [
@@ -43,7 +43,7 @@ class ServiceMappingCollector
* Groups board, ski passes, rentals, rental insurance, courses, parking, and additional services
* by service ID with their participant assignments (1-based).
*
* @return array<string, array<int>> Map of service ID to participant IDs
* @return array<int, list<int>> Map of service ID to participant IDs
*/
public function collectServiceMappings(BookingDto $bookingDto): array
{
@@ -99,7 +99,7 @@ class ServiceMappingCollector
/**
* Collects transportation service mappings.
*
* @return array<string, array<int>> Map of transportation service ID to participant IDs
* @return array<int, list<int>> Map of transportation service ID to participant IDs
*/
public function collectTransportationMappings(BookingDto $bookingDto): array
{
@@ -126,7 +126,7 @@ class ServiceMappingCollector
* Collects the unified pickup selection which applies to both directions.
* The API receives this as outbound pickup data.
*
* @return array<string, array<int>> Map of pickup ID to participant IDs
* @return array<int, list<int>> Map of pickup ID to participant IDs
*/
public function collectPickupMappings(BookingDto $bookingDto): array
{
@@ -148,7 +148,7 @@ class ServiceMappingCollector
*
* Collects the drop-off selection for the inbound/return direction.
*
* @return array<string, array<int>> Map of drop-off ID to participant IDs
* @return array<int, list<int>> Map of drop-off ID to participant IDs
*/
public function collectDropOffMappings(BookingDto $bookingDto): array
{
@@ -171,7 +171,7 @@ class ServiceMappingCollector
* CRITICAL: Insurance data is only included in CREATE flow, not in UPDATE flow.
* IMPORTANT: Excludes synthetic "no insurance" option from BPN transmission.
*
* @return array<string, array<int>> Map of insurance ID to participant IDs
* @return array<string, list<int>> Map of insurance ID to participant IDs
*/
public function collectInsuranceMappings(BookingDto $bookingDto): array
{
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\Exception;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\Exception;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\Form\ChoiceLoader;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\Form;
+9 -2
View File
@@ -182,8 +182,15 @@ class Insurance
$urls = [];
foreach ($this->containedInsurances as $containedInsurance) {
if (null !== $containedInsurance->{$urlProperty}) {
$urls[] = $containedInsurance->{$urlProperty};
$url = match ($urlProperty) {
'urlInfo' => $containedInsurance->urlInfo,
'urlProductInfo' => $containedInsurance->urlProductInfo,
'urlTerms' => $containedInsurance->urlTerms,
default => null,
};
if (null !== $url) {
$urls[] = $url;
}
}
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\Model;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\Traits;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\Utility;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\Utility;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlLoader;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser;
+4 -2
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\BusProNet\XmlParser;
@@ -15,10 +16,11 @@ class ProductsParser extends AbstractParser
$result
->filterXPath('//produkte/produkt')
->each(function (Crawler $node) use (&$products) {
if (false === empty($node->attr('code'))) {
$code = $node->attr('code');
if (null !== $code && '' !== $code) {
$product = new Product();
$product->id = (int) $node->attr('id');
$product->code = (string) $node->attr('code');
$product->code = $code;
$product->name = (string) $node->attr('bezeichnung');
$products[$product->id] = $product;
+5 -2
View File
@@ -50,8 +50,11 @@ class BpnFetchTravelCommand extends Command
$io = new SymfonyStyle($input, $output);
$travelId = (int) $input->getArgument('travel-id');
$filename = $input->getOption('filename') ?? sprintf('Ziel_%d.xml', $travelId);
$dryRun = $input->getOption('dry-run');
$filenameOption = $input->getOption('filename');
$filename = is_string($filenameOption) && '' !== $filenameOption
? $filenameOption
: sprintf('Ziel_%d.xml', $travelId);
$dryRun = true === $input->getOption('dry-run');
$io->text(sprintf('Fetching travel data for product ID %d...', $travelId));
+14 -4
View File
@@ -39,7 +39,14 @@ class BpnReplayCommand extends Command
{
$io = new SymfonyStyle($input, $output);
$filePath = $input->getArgument('file');
$filePathArgument = $input->getArgument('file');
if (!is_string($filePathArgument) || '' === $filePathArgument) {
$io->error('Invalid file path argument.');
return Command::FAILURE;
}
$filePath = $filePathArgument;
if (false === file_exists($filePath)) {
$io->error(sprintf('File not found: %s', $filePath));
@@ -84,8 +91,9 @@ class BpnReplayCommand extends Command
return Command::FAILURE;
}
$outputFile = $input->getOption('output');
if (null !== $outputFile) {
$outputFileOption = $input->getOption('output');
if (is_string($outputFileOption) && '' !== $outputFileOption) {
$outputFile = $outputFileOption;
if (false === file_put_contents($outputFile, $response)) {
$io->error(sprintf('Failed to write response to: %s', $outputFile));
@@ -139,6 +147,8 @@ class BpnReplayCommand extends Command
return $xml;
}
return $doc->saveXML() ?: $xml;
$formattedXml = $doc->saveXML();
return false === $formattedXml ? $xml : $formattedXml;
}
}
+2 -2
View File
@@ -65,8 +65,8 @@ class BpnXmlSyncCommand extends Command
return Command::SUCCESS;
}
$force = $input->getOption('force');
$dryRun = $input->getOption('dry-run');
$force = true === $input->getOption('force');
$dryRun = true === $input->getOption('dry-run');
$remoteInfo = $this->readRemoteInfo();
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Command;
@@ -41,7 +41,7 @@ class DraftBackfillBookingNumberCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$dryRun = $input->getOption('dry-run');
$dryRun = true === $input->getOption('dry-run');
$drafts = $this->draftRepository->findBy(['bookingNumber' => null]);
@@ -80,7 +80,7 @@ class DraftBackfillBookingNumberCommand extends Command
continue;
}
if ($dryRun) {
if (true === $dryRun) {
$io->text(sprintf(
'[DRY RUN] Would set booking number %d for draft %d (bookingId %d)',
$bookingNumber,
@@ -108,7 +108,7 @@ class DraftBackfillBookingNumberCommand extends Command
]
);
if ($dryRun) {
if (true === $dryRun) {
$io->note('Dry run - no changes were made.');
} else {
$io->success(sprintf('Updated %d draft(s).', $updated));
+2 -2
View File
@@ -38,9 +38,9 @@ class DraftCleanupCommand extends Command
protected function execute(InputInterface $input, OutputInterface $output): int
{
$io = new SymfonyStyle($input, $output);
$dryRun = $input->getOption('dry-run');
$dryRun = true === $input->getOption('dry-run');
if ($dryRun) {
if (true === $dryRun) {
$expiredDrafts = $this->draftRepository->createQueryBuilder('d')
->where('d.travelDate < :today')
->setParameter('today', new \DateTimeImmutable('today'))
+9 -6
View File
@@ -38,9 +38,12 @@ class DraftInspectCommand extends Command
{
$io = new SymfonyStyle($input, $output);
$days = $input->getOption('days');
$bookingId = $input->getOption('booking-id');
$shouldDelete = $input->getOption('delete');
$daysOption = $input->getOption('days');
$bookingIdOption = $input->getOption('booking-id');
$shouldDelete = true === $input->getOption('delete');
$days = is_numeric($daysOption) ? (int) $daysOption : null;
$bookingId = is_numeric($bookingIdOption) ? (int) $bookingIdOption : null;
$qb = $this->draftRepository->createQueryBuilder('d')
->join('d.user', 'u')
@@ -48,14 +51,14 @@ class DraftInspectCommand extends Command
->orderBy('d.updatedAt', 'DESC');
if (null !== $days) {
$cutoffDate = new \DateTimeImmutable(sprintf('-%d days', (int) $days));
$cutoffDate = new \DateTimeImmutable(sprintf('-%d days', $days));
$qb->andWhere('d.updatedAt < :cutoff')
->setParameter('cutoff', $cutoffDate);
}
if (null !== $bookingId) {
$qb->andWhere('d.bookingId = :bookingId')
->setParameter('bookingId', (int) $bookingId);
->setParameter('bookingId', $bookingId);
}
$drafts = $qb->getQuery()->getResult();
@@ -97,7 +100,7 @@ class DraftInspectCommand extends Command
'Drafts are preserved when: user cancels, API rejects submission, or user simply leaves',
]);
if ($shouldDelete) {
if (true === $shouldDelete) {
if (false === $io->confirm(sprintf('Are you sure you want to delete %d draft(s)?', \count($drafts)), false)) {
$io->warning('Deletion cancelled.');
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Command;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Admin;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
+3 -2
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
@@ -26,13 +27,13 @@ class LastUpdateController extends AbstractController
$lines = explode("\n", $file);
$line = trim($lines[0]);
if (true === empty($line)) {
if ('' === $line) {
$lastUploadAt = null;
} else {
$lastUploadAt = \DateTime::createFromFormat('d.m.Y H:i:s', $line);
}
}
return $this->json(['timestamp' => $lastUploadAt->format('Y-m-d H:i:s')]);
return $this->json(['timestamp' => $lastUploadAt?->format('Y-m-d H:i:s')]);
}
}
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
+4 -1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
@@ -64,7 +65,9 @@ class TravelController extends AbstractController
$dateCode = (new DateCodeUtility())->sanitize($dateCode);
$dateId = $this->travelDataService->mapDateCodeToId($dateCode);
$hotelId = $hotelCode ? $this->travelDataService->mapHotelCodeToId($hotelCode) : null;
$hotelId = null !== $hotelCode && '' !== $hotelCode
? $this->travelDataService->mapHotelCodeToId($hotelCode)
: null;
if (null === $dateId) {
return $this->json(['message' => 'Not found'], Response::HTTP_NOT_FOUND);
+2 -1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Api;
@@ -80,7 +81,7 @@ class UserinfoController extends AbstractController
// filter claims by provided scopes
foreach ($scopes as $scope) {
if (false === in_array($scope, $keys)) {
if (false === in_array($scope, $keys, true)) {
continue;
}
$claims[$scope] = $allClaims[$scope];
@@ -53,7 +53,8 @@ class Step1Controller extends AbstractController
$oldRoomSelectionSnapshot = $this->bookingSessionService->getOrCreateBaselineSnapshot($request, $bookingCreateDto);
// Validate step access - allow step 1 or redirect to current step
if ($redirect = $this->validateStepAccess($bookingCreateDto, 1)) {
$redirect = $this->validateStepAccess($bookingCreateDto, 1);
if (null !== $redirect) {
return $redirect;
}
@@ -61,7 +61,8 @@ class Step4Controller extends AbstractController
$bookingCreateDto = $result;
// Validate step access
if ($redirect = $this->validateStepAccess($bookingCreateDto, 4)) {
$redirect = $this->validateStepAccess($bookingCreateDto, 4);
if (null !== $redirect) {
return $redirect;
}
@@ -218,6 +219,22 @@ class Step4Controller extends AbstractController
return false !== filter_var($normalizedEmail, FILTER_VALIDATE_EMAIL) ? $normalizedEmail : null;
}
/**
* @param array<string, mixed> $context
*/
private function handleApiError(
string $logMessage,
array $context,
string $flashMessage,
BookingDto $bookingCreateDto,
FormInterface $form,
): Response {
$this->logger->error($logMessage, $context);
$this->addFlash('error', $flashMessage);
return $this->renderStepForm($bookingCreateDto, $form);
}
/**
* Clears travel data and availability cache after successful booking.
*/
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller\Booking;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller;
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Controller;
@@ -0,0 +1,45 @@
<?php
declare(strict_types=1);
namespace App\DependencyInjection\Env;
use Symfony\Component\DependencyInjection\EnvVarProcessorInterface;
use Symfony\Component\DependencyInjection\Exception\RuntimeException;
final class CsvIntEnvVarProcessor implements EnvVarProcessorInterface
{
/**
* @return array<int, int>
*/
public function getEnv(string $prefix, string $name, \Closure $getEnv): array
{
$raw = $getEnv($name);
if (!\is_string($raw)) {
throw new RuntimeException(sprintf('The "csv_int" env var processor expects a string, got "%s" for "%s".', get_debug_type($raw), $name));
}
$values = array_map('trim', explode(',', $raw));
if ([] !== array_filter($values, static fn (string $value): bool => '' === $value)) {
throw new RuntimeException(sprintf('Environment variable "%s" contains empty values and cannot be processed as csv_int.', $name));
}
$integers = array_map(static function (string $value) use ($name): int {
if (1 !== preg_match('/^[+-]?\d+$/', $value)) {
throw new RuntimeException(sprintf('Environment variable "%s" contains non-integer value "%s".', $name, $value));
}
return (int) $value;
}, $values);
return $integers;
}
public static function getProvidedTypes(): array
{
return [
'csv_int' => 'array',
];
}
}
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Form;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Form;
@@ -154,7 +154,7 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
}
}
if (false === empty($attributes)) {
if ([] !== $attributes) {
$stateModifications['attr'] = $attributes;
}
@@ -170,7 +170,7 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
*/
public function hasStateConditions(string $fieldName): bool
{
return isset($this->fieldStateConditions[$fieldName]) && !empty($this->fieldStateConditions[$fieldName]);
return isset($this->fieldStateConditions[$fieldName]) && [] !== $this->fieldStateConditions[$fieldName];
}
/**
@@ -210,7 +210,7 @@ abstract class AbstractFieldStateProvider implements FieldStateProviderInterface
foreach (array_keys($this->fieldStateConditions) as $fieldName) {
$fieldState = $this->getFieldState($fieldName, $bookingDto, $participantIndex, $formData);
if (!empty($fieldState)) {
if ([] !== $fieldState) {
$allStates[$fieldName] = $fieldState;
}
}
@@ -130,7 +130,7 @@ abstract class AbstractParticipantFieldHandler implements ParticipantFieldHandle
*/
protected function normalizeEmptyValue(mixed $value): mixed
{
return empty($value) ? null : $value;
return $this->isEmptyValue($value) ? null : $value;
}
/**
@@ -146,13 +146,24 @@ abstract class AbstractParticipantFieldHandler implements ParticipantFieldHandle
*/
protected function normalizeIntValue(mixed $value): ?int
{
if (empty($value)) {
if ($this->isEmptyValue($value)) {
return null;
}
return is_numeric($value) ? (int) $value : null;
}
private function isEmptyValue(mixed $value): bool
{
return null === $value
|| false === $value
|| 0 === $value
|| 0.0 === $value
|| '' === $value
|| '0' === $value
|| [] === $value;
}
/**
* Normalizes a checkbox/form toggle value to a boolean.
*
@@ -37,7 +37,7 @@ class CompositeCondition implements FieldConditionInterface
private const OPERATOR_OR = 'OR';
private const OPERATOR_NOT = 'NOT';
/** @var FieldConditionInterface[] */
/** @var non-empty-list<FieldConditionInterface> */
private readonly array $conditions;
/**
@@ -131,7 +131,7 @@ class CompositeCondition implements FieldConditionInterface
*/
public static function and(FieldConditionInterface ...$conditions): self
{
if (empty($conditions)) {
if ([] === $conditions) {
throw new \InvalidArgumentException('AND condition requires at least one condition');
}
@@ -145,7 +145,7 @@ class CompositeCondition implements FieldConditionInterface
*/
public static function or(FieldConditionInterface ...$conditions): self
{
if (empty($conditions)) {
if ([] === $conditions) {
throw new \InvalidArgumentException('OR condition requires at least one condition');
}
@@ -167,6 +167,8 @@ class CompositeCondition implements FieldConditionInterface
*
* Returns false as soon as any condition evaluates to false,
* avoiding unnecessary evaluation of remaining conditions.
*
* @param array<string, mixed> $formData
*/
/** @param array<string, mixed> $formData */
private function evaluateAnd(BookingDto $bookingDto, int $participantIndex, array $formData): bool
@@ -185,6 +187,8 @@ class CompositeCondition implements FieldConditionInterface
*
* Returns true as soon as any condition evaluates to true,
* avoiding unnecessary evaluation of remaining conditions.
*
* @param array<string, mixed> $formData
*/
/** @param array<string, mixed> $formData */
private function evaluateOr(BookingDto $bookingDto, int $participantIndex, array $formData): bool
@@ -200,6 +204,8 @@ class CompositeCondition implements FieldConditionInterface
/**
* Evaluates NOT logic by inverting the result of the single condition.
*
* @param array<string, mixed> $formData
*/
/** @param array<string, mixed> $formData */
private function evaluateNot(BookingDto $bookingDto, int $participantIndex, array $formData): bool
@@ -221,6 +227,8 @@ class CompositeCondition implements FieldConditionInterface
/**
* Validates condition count based on operator requirements.
*
* @param list<FieldConditionInterface> $conditions
*/
/** @param list<mixed> $conditions */
private function validateConditionCount(string $operator, array $conditions): void
@@ -174,6 +174,8 @@ class FieldValueCondition implements FieldConditionInterface
/**
* Retrieves field value from form data or participant data.
*
* @param array<string, mixed> $formData
*/
/** @param array<string, mixed> $formData */
private function getFieldValue(array $formData, int $participantIndex, BookingDto $bookingDto): mixed
@@ -186,7 +188,9 @@ class FieldValueCondition implements FieldConditionInterface
// Then check participant DTO data
$participant = $bookingDto->getParticipant($participantIndex);
if (null !== $participant && property_exists($participant, $this->fieldName)) {
return $participant->{$this->fieldName};
$participantValues = get_object_vars($participant);
return $participantValues[$this->fieldName] ?? null;
}
// Finally check booking-level form data
@@ -212,6 +216,8 @@ class FieldValueCondition implements FieldConditionInterface
/**
* Checks if field value is in array of expected values.
*
* @param array<int, mixed> $expectedValues
*/
/** @param list<mixed> $expectedValues */
private function compareIn(mixed $fieldValue, array $expectedValues): bool
@@ -230,7 +236,7 @@ class FieldValueCondition implements FieldConditionInterface
*/
private function isEmpty(mixed $value): bool
{
return null === $value || '' === $value || (is_array($value) && empty($value));
return null === $value || '' === $value || [] === $value;
}
/**
@@ -33,7 +33,7 @@ class RentalInsuranceAvailableCondition implements FieldConditionInterface
true
);
return false === empty($services);
return [] !== $services;
}
/**
@@ -31,6 +31,10 @@ class RoomSelectionCondition implements FieldConditionInterface
*/
public function __construct(array $requiredRoomCodes, string $matchMode = self::MATCH_MODE_PREFIX)
{
if (!in_array($matchMode, [self::MATCH_MODE_PREFIX, self::MATCH_MODE_EXACT], true)) {
throw new \InvalidArgumentException(sprintf('Invalid match mode "%s".', $matchMode));
}
$this->requiredRoomCodes = array_map('strtolower', $requiredRoomCodes);
$this->matchMode = $matchMode;
}
@@ -6,6 +6,7 @@ namespace App\Form\Service\Condition;
use App\BusProNet\Model\Service;
use App\Form\Model\BookingDto;
use App\Form\Model\ParticipantDto;
use App\Form\Service\Contract\FieldConditionInterface;
/**
@@ -103,7 +104,7 @@ class ServiceSubTypeCondition implements FieldConditionInterface
{
$subTypeDescription = is_array($this->expectedSubType)
? '['.implode(', ', $this->expectedSubType).']'
: (string) $this->expectedSubType;
: $this->expectedSubType;
return match ($this->operator) {
self::OPERATOR_EQUALS => sprintf('Service "%s" sub-type equals %s', $this->serviceFieldName, $subTypeDescription),
@@ -160,6 +161,8 @@ class ServiceSubTypeCondition implements FieldConditionInterface
/**
* Retrieves service from form data or participant data.
*
* @param array<string, mixed> $formData
*/
/** @param array<string, mixed> $formData */
private function getService(array $formData, int $participantIndex, BookingDto $bookingDto): ?Service
@@ -176,15 +179,30 @@ class ServiceSubTypeCondition implements FieldConditionInterface
// Then check participant DTO data
$participant = $bookingDto->getParticipant($participantIndex);
if (null !== $participant && property_exists($participant, $this->serviceFieldName)) {
return $participant->{$this->serviceFieldName};
if (null !== $participant) {
return $this->getServiceFromParticipant($participant);
}
return null;
}
private function getServiceFromParticipant(ParticipantDto $participant): ?Service
{
return match ($this->serviceFieldName) {
'skiPass' => $participant->skiPass,
'rentalInsurance' => $participant->rentalInsurance,
'transportationOutbound' => $participant->transportationOutbound,
'transportationInbound' => $participant->transportationInbound,
'veg' => $participant->veg,
'parkingService' => $participant->parkingService,
default => null,
};
}
/**
* Checks if sub-type is in the expected array.
*
* @param string|list<string> $expectedSubTypes
*/
/** @param string|list<string> $expectedSubTypes */
private function isSubTypeIn(string $actualSubType, string|array $expectedSubTypes): bool
@@ -215,6 +233,8 @@ class ServiceSubTypeCondition implements FieldConditionInterface
/**
* Validates expected sub-type based on operator requirements.
*
* @param string|list<string> $expectedSubType
*/
/** @param string|list<string> $expectedSubType */
private function validateExpectedSubType(string $operator, string|array $expectedSubType): void
@@ -274,7 +274,8 @@ class ParticipantFieldHandlerRegistry
continue;
}
$dtoValue = $participant->{$fieldName};
$participantValues = get_object_vars($participant);
$dtoValue = $participantValues[$fieldName] ?? null;
$submittedValue = $participantData[$fieldName];
// Check if the DTO value differs from submitted value
@@ -427,7 +428,7 @@ class ParticipantFieldHandlerRegistry
}
// Process handlers in dependency order
while (false === empty($queue)) {
while ([] !== $queue) {
$current = array_shift($queue);
$result[] = $current;
@@ -517,7 +518,8 @@ class ParticipantFieldHandlerRegistry
// Only sync fields that were in the original submission
// This prevents adding extra fields that would cause validation errors
if (property_exists($participant, $fieldName) && array_key_exists($fieldName, $participantData)) {
$dtoValue = $participant->{$fieldName};
$participantValues = get_object_vars($participant);
$dtoValue = $participantValues[$fieldName] ?? null;
$participantData[$fieldName] = $this->convertDtoValueToSubmittedFormat($dtoValue);
}
}
@@ -126,7 +126,7 @@ class ParticipantParkingFieldHandler extends AbstractParticipantFieldHandler
Constants::TOKEN_PARKING
);
if (empty($parkingServices)) {
if ([] === $parkingServices) {
return null;
}
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App\Form;
+1
View File
@@ -1,4 +1,5 @@
<?php
declare(strict_types=1);
namespace App;
+4 -4
View File
@@ -51,7 +51,7 @@ class BpnXmlAnonymizer
continue;
}
$personId = $this->resolvePersonId($personNode, (int) $index);
$personId = $this->resolvePersonId($personNode, $index);
$identity = $this->getOrCreateIdentity($personId);
$this->replaceNameFields($personNode, $identity);
@@ -96,7 +96,7 @@ class BpnXmlAnonymizer
}
if (true === $personNode->hasAttribute('id')) {
$attributeValue = trim((string) $personNode->getAttribute('id'));
$attributeValue = trim($personNode->getAttribute('id'));
if ('' !== $attributeValue) {
return sprintf('attr-id:%s', $attributeValue);
}
@@ -115,7 +115,7 @@ class BpnXmlAnonymizer
$nameValues[] = trim($nameNode->textContent);
}
if (true === $personNode->hasAttribute('name')) {
$nameValues[] = trim((string) $personNode->getAttribute('name'));
$nameValues[] = trim($personNode->getAttribute('name'));
}
$firstNameNode = $this->getDirectChild($personNode, 'vorname');
@@ -123,7 +123,7 @@ class BpnXmlAnonymizer
$nameValues[] = trim($firstNameNode->textContent);
}
if (true === $personNode->hasAttribute('vorname')) {
$nameValues[] = trim((string) $personNode->getAttribute('vorname'));
$nameValues[] = trim($personNode->getAttribute('vorname'));
}
$communicationNode = $this->getDirectChild($personNode, 'kommunikation');
+2 -2
View File
@@ -136,7 +136,7 @@ class InsuranceManager
$eligibleInsurances = $this->getEligibleInsurances($sameTypeInsurances, $participant, $booking, $travelPrice);
// Return the first eligible insurance (they should all be equivalent for the same type)
return !empty($eligibleInsurances) ? array_values($eligibleInsurances)[0] : null;
return [] !== $eligibleInsurances ? array_values($eligibleInsurances)[0] : null;
}
/**
@@ -168,7 +168,7 @@ class InsuranceManager
foreach ($booking->getParticipants() as $index => $participant) {
$travelPrice = $participantPrices[$index] ?? 0.0;
$eligibleInsurances = $this->getEligibleInsurances($sameTypeInsurances, $participant, $booking, $travelPrice);
$assignments[$index] = !empty($eligibleInsurances) ? array_values($eligibleInsurances)[0] : null;
$assignments[$index] = [] !== $eligibleInsurances ? array_values($eligibleInsurances)[0] : null;
}
return $assignments;
@@ -70,7 +70,7 @@ class ParticipantEligibilityChecker
fn (Service $service) => $this->isSkiPassAvailableForParticipant($service, $bookingDto, $participantIndex)
);
return !empty($availableSkiPasses);
return [] !== $availableSkiPasses;
})();
}
+8
View File
@@ -282,6 +282,14 @@ class AppRuntime implements RuntimeExtensionInterface
);
}
private function renderIcon(Environment $environment, string $icon, ?string $classes): string
{
return $environment->render('_partials/_icon.html.twig', [
'icon' => $icon,
'class' => $classes ?? 'w-4 h-4',
]);
}
/**
* Gets the appropriate field state provider based on booking mode.
*
@@ -63,7 +63,7 @@ class MandatoryAdditionalServicesSelectedValidator extends ConstraintValidator
}
);
if (true === empty($mandatoryAdditionalServices)) {
if ([] === $mandatoryAdditionalServices) {
return;
}
@@ -67,7 +67,7 @@ class ParticipantValidator extends ConstraintValidator
public function assertBodyDimensionsWhenRentalsSelected(ParticipantDto $participant): void
{
// Check if any rental services are selected
if (true === empty($participant->rentals)) {
if ([] === $participant->rentals) {
return;
}