wip: refactoring
This commit is contained in:
Generated
+13
-12
@@ -1758,16 +1758,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/flysystem-bundle",
|
"name": "league/flysystem-bundle",
|
||||||
"version": "3.4.0",
|
"version": "3.5.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/thephpleague/flysystem-bundle.git",
|
"url": "https://github.com/thephpleague/flysystem-bundle.git",
|
||||||
"reference": "6493f7f2ab49bc5817e4b064b9b971d93faabc12"
|
"reference": "bf5ab3c072c0def47872e28f69876548d8669657"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/thephpleague/flysystem-bundle/zipball/6493f7f2ab49bc5817e4b064b9b971d93faabc12",
|
"url": "https://api.github.com/repos/thephpleague/flysystem-bundle/zipball/bf5ab3c072c0def47872e28f69876548d8669657",
|
||||||
"reference": "6493f7f2ab49bc5817e4b064b9b971d93faabc12",
|
"reference": "bf5ab3c072c0def47872e28f69876548d8669657",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -1791,6 +1791,7 @@
|
|||||||
"league/flysystem-read-only": "^3.15",
|
"league/flysystem-read-only": "^3.15",
|
||||||
"league/flysystem-sftp-v3": "^3.1",
|
"league/flysystem-sftp-v3": "^3.1",
|
||||||
"league/flysystem-webdav": "^3.29",
|
"league/flysystem-webdav": "^3.29",
|
||||||
|
"platformcommunity/flysystem-bunnycdn": "^3.3",
|
||||||
"symfony/dotenv": "^5.4 || ^6.0 || ^7.0",
|
"symfony/dotenv": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0",
|
"symfony/framework-bundle": "^5.4 || ^6.0 || ^7.0",
|
||||||
"symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0",
|
"symfony/phpunit-bridge": "^5.4 || ^6.0 || ^7.0",
|
||||||
@@ -1822,9 +1823,9 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/thephpleague/flysystem-bundle/issues",
|
"issues": "https://github.com/thephpleague/flysystem-bundle/issues",
|
||||||
"source": "https://github.com/thephpleague/flysystem-bundle/tree/3.4.0"
|
"source": "https://github.com/thephpleague/flysystem-bundle/tree/3.5.0"
|
||||||
},
|
},
|
||||||
"time": "2025-01-23T18:07:31+00:00"
|
"time": "2025-06-22T12:43:01+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "league/flysystem-local",
|
"name": "league/flysystem-local",
|
||||||
@@ -10425,16 +10426,16 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "friendsofphp/php-cs-fixer",
|
"name": "friendsofphp/php-cs-fixer",
|
||||||
"version": "v3.82.2",
|
"version": "v3.84.0",
|
||||||
"source": {
|
"source": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
"url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git",
|
||||||
"reference": "684ed3ab41008a2a4848de8bde17eb168c596247"
|
"reference": "38dad0767bf2a9b516b976852200ae722fe984ca"
|
||||||
},
|
},
|
||||||
"dist": {
|
"dist": {
|
||||||
"type": "zip",
|
"type": "zip",
|
||||||
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/684ed3ab41008a2a4848de8bde17eb168c596247",
|
"url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/38dad0767bf2a9b516b976852200ae722fe984ca",
|
||||||
"reference": "684ed3ab41008a2a4848de8bde17eb168c596247",
|
"reference": "38dad0767bf2a9b516b976852200ae722fe984ca",
|
||||||
"shasum": ""
|
"shasum": ""
|
||||||
},
|
},
|
||||||
"require": {
|
"require": {
|
||||||
@@ -10518,7 +10519,7 @@
|
|||||||
],
|
],
|
||||||
"support": {
|
"support": {
|
||||||
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
"issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues",
|
||||||
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.82.2"
|
"source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.84.0"
|
||||||
},
|
},
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
@@ -10526,7 +10527,7 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"time": "2025-07-08T21:13:15+00:00"
|
"time": "2025-07-15T18:21:57+00:00"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "myclabs/deep-copy",
|
"name": "myclabs/deep-copy",
|
||||||
|
|||||||
@@ -62,3 +62,7 @@ services:
|
|||||||
$logger: '@monolog.logger.core'
|
$logger: '@monolog.logger.core'
|
||||||
$preferRemote: '%env(bool:APP_TRAVEL_PREFER_REMOTE)%'
|
$preferRemote: '%env(bool:APP_TRAVEL_PREFER_REMOTE)%'
|
||||||
$enableFallback: '%env(bool:APP_TRAVEL_ENABLE_FALLBACK)%'
|
$enableFallback: '%env(bool:APP_TRAVEL_ENABLE_FALLBACK)%'
|
||||||
|
|
||||||
|
App\Form\Service\ParticipantRoomChoiceLoaderFactory:
|
||||||
|
arguments:
|
||||||
|
$choiceListFactory: '@form.choice_list_factory.default'
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use App\BusProNet\Form\CountryType;
|
use App\BusProNet\Form\CountryType;
|
||||||
|
use App\Form\Model\BookingCreateDto;
|
||||||
use App\Form\Model\ParticipantDto;
|
use App\Form\Model\ParticipantDto;
|
||||||
use App\Form\Service\ParticipantRoomChoiceLoaderFactory;
|
use App\Form\Service\ParticipantRoomChoiceLoaderFactory;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
@@ -27,9 +28,11 @@ class BookingCreateParticipantType extends AbstractType
|
|||||||
$builder
|
$builder
|
||||||
->add('firstName', TextType::class, [
|
->add('firstName', TextType::class, [
|
||||||
'label' => 'Vorname',
|
'label' => 'Vorname',
|
||||||
|
'clean_xss' => true,
|
||||||
])
|
])
|
||||||
->add('lastName', TextType::class, [
|
->add('lastName', TextType::class, [
|
||||||
'label' => 'Nachname',
|
'label' => 'Nachname',
|
||||||
|
'clean_xss' => true,
|
||||||
])
|
])
|
||||||
->add('dateOfBirth', BirthdayType::class, [
|
->add('dateOfBirth', BirthdayType::class, [
|
||||||
'label' => 'Geburtsdatum',
|
'label' => 'Geburtsdatum',
|
||||||
@@ -55,19 +58,36 @@ class BookingCreateParticipantType extends AbstractType
|
|||||||
->add('email', EmailType::class, [
|
->add('email', EmailType::class, [
|
||||||
'label' => 'E-Mail',
|
'label' => 'E-Mail',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
'clean_xss' => true,
|
||||||
])
|
])
|
||||||
->add('mobile', TextType::class, [
|
->add('mobile', TextType::class, [
|
||||||
'label' => 'Telefon (mobil)',
|
'label' => 'Telefon (mobil)',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
|
'clean_xss' => true,
|
||||||
])
|
])
|
||||||
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) {
|
->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) use ($options) {
|
||||||
$data = $event->getData();
|
/** @var ParticipantDto|null $participantData */
|
||||||
|
$participantData = $event->getData();
|
||||||
$form = $event->getForm();
|
$form = $event->getForm();
|
||||||
|
|
||||||
$choiceLoader = $this->choiceLoaderFactory->createChoiceLoader(
|
if (null === $participantData) {
|
||||||
$options['participant_adapter'],
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse up the form tree to get the root form's data.
|
||||||
|
$rootForm = $form;
|
||||||
|
while ($rootForm->getParent()) {
|
||||||
|
$rootForm = $rootForm->getParent();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var BookingCreateDto $bookingCreateDto */
|
||||||
|
$bookingCreateDto = $rootForm->getData();
|
||||||
|
$allParticipants = $bookingCreateDto->participants;
|
||||||
|
|
||||||
|
$choiceLoader = $this->choiceLoaderFactory->create(
|
||||||
|
$allParticipants,
|
||||||
$options['selected_rooms'],
|
$options['selected_rooms'],
|
||||||
$data->index
|
$participantData->index
|
||||||
);
|
);
|
||||||
|
|
||||||
$form->add('assignedRoomId', ChoiceType::class, [
|
$form->add('assignedRoomId', ChoiceType::class, [
|
||||||
@@ -83,8 +103,9 @@ class BookingCreateParticipantType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => ParticipantDto::class,
|
'data_class' => ParticipantDto::class,
|
||||||
'participant_adapter' => null,
|
|
||||||
'selected_rooms' => [],
|
'selected_rooms' => [],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$resolver->setAllowedTypes('selected_rooms', 'array');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,61 +2,37 @@
|
|||||||
|
|
||||||
namespace App\Form;
|
namespace App\Form;
|
||||||
|
|
||||||
use App\Form\DataAdapters\ParticipantDataAdapter;
|
|
||||||
use App\Form\Model\BookingCreateDto;
|
use App\Form\Model\BookingCreateDto;
|
||||||
use Symfony\Component\Form\AbstractType;
|
use Symfony\Component\Form\AbstractType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\Form\FormEvent;
|
use Symfony\Component\Form\FormEvent;
|
||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\Form\FormInterface;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
class BookingCreateStep2Type extends AbstractType
|
class BookingCreateStep2Type extends AbstractType
|
||||||
{
|
{
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
$builder->addEventListener(FormEvents::PRE_SET_DATA, [$this, 'onPreSetData']);
|
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
|
||||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, [$this, 'onPreSubmit']);
|
/** @var BookingCreateDto|null $data */
|
||||||
}
|
|
||||||
|
|
||||||
public function onPreSetData(FormEvent $event): void
|
|
||||||
{
|
|
||||||
/** @var BookingCreateDto $data */
|
|
||||||
$data = $event->getData();
|
$data = $event->getData();
|
||||||
$form = $event->getForm();
|
$form = $event->getForm();
|
||||||
|
|
||||||
$adapter = new ParticipantDataAdapter($data->participants);
|
if (null === $data) {
|
||||||
$this->addParticipantsField($form, $adapter, $data->getSelectedRooms());
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function onPreSubmit(FormEvent $event): void
|
|
||||||
{
|
|
||||||
$data = $event->getData();
|
|
||||||
$form = $event->getForm();
|
|
||||||
/** @var BookingCreateDto $bookingCreateDto */
|
|
||||||
$bookingCreateDto = $form->getData();
|
|
||||||
|
|
||||||
$adapter = new ParticipantDataAdapter($data['participants']);
|
|
||||||
$form->remove('participants');
|
|
||||||
$this->addParticipantsField($form, $adapter, $bookingCreateDto->getSelectedRooms());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds the participants collection field to the form.
|
|
||||||
*/
|
|
||||||
private function addParticipantsField(FormInterface $form, ParticipantDataAdapter $adapter, array $selectedRooms): void
|
|
||||||
{
|
|
||||||
$form->add('participants', CollectionType::class, [
|
$form->add('participants', CollectionType::class, [
|
||||||
'entry_type' => BookingCreateParticipantType::class,
|
'entry_type' => BookingCreateParticipantType::class,
|
||||||
'allow_add' => false,
|
'allow_add' => false,
|
||||||
'allow_delete' => false,
|
'allow_delete' => false,
|
||||||
'by_reference' => false,
|
'by_reference' => false,
|
||||||
'entry_options' => [
|
'entry_options' => [
|
||||||
'participant_adapter' => $adapter,
|
'selected_rooms' => $data->getSelectedRooms(),
|
||||||
'selected_rooms' => $selectedRooms,
|
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
|||||||
@@ -42,12 +42,14 @@ class BookingEditParticipantType extends AbstractType
|
|||||||
'attr' => [
|
'attr' => [
|
||||||
'readonly' => false === $personalDataMutable,
|
'readonly' => false === $personalDataMutable,
|
||||||
],
|
],
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('lastName', TextType::class, [
|
->add('lastName', TextType::class, [
|
||||||
'label' => 'Nachname',
|
'label' => 'Nachname',
|
||||||
'attr' => [
|
'attr' => [
|
||||||
'readonly' => false === $personalDataMutable,
|
'readonly' => false === $personalDataMutable,
|
||||||
],
|
],
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('dateOfBirth', BirthdayType::class, [
|
->add('dateOfBirth', BirthdayType::class, [
|
||||||
'label' => 'Geburtsdatum',
|
'label' => 'Geburtsdatum',
|
||||||
@@ -87,6 +89,7 @@ class BookingEditParticipantType extends AbstractType
|
|||||||
'attr' => [
|
'attr' => [
|
||||||
'readonly' => true === $isApplicant,
|
'readonly' => true === $isApplicant,
|
||||||
],
|
],
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('mobile', TextType::class, [
|
->add('mobile', TextType::class, [
|
||||||
'label' => 'Telefon (mobil)',
|
'label' => 'Telefon (mobil)',
|
||||||
@@ -94,6 +97,7 @@ class BookingEditParticipantType extends AbstractType
|
|||||||
'attr' => [
|
'attr' => [
|
||||||
'readonly' => true === $isApplicant,
|
'readonly' => true === $isApplicant,
|
||||||
],
|
],
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('height', ChoiceType::class, [
|
->add('height', ChoiceType::class, [
|
||||||
'label' => 'Körpergröße',
|
'label' => 'Körpergröße',
|
||||||
@@ -379,7 +383,6 @@ class BookingEditParticipantType extends AbstractType
|
|||||||
'transportation_services_mutable' => true,
|
'transportation_services_mutable' => true,
|
||||||
'pickups_mutable' => true,
|
'pickups_mutable' => true,
|
||||||
'applicant_id' => null,
|
'applicant_id' => null,
|
||||||
'anti_xss' => true,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ use Symfony\Component\Form\FormEvent;
|
|||||||
use Symfony\Component\Form\FormEvents;
|
use Symfony\Component\Form\FormEvents;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
final class BookingEditType extends AbstractType
|
class BookingEditType extends AbstractType
|
||||||
{
|
{
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
{
|
{
|
||||||
@@ -69,7 +69,6 @@ final class BookingEditType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => BookingEditDto::class,
|
'data_class' => BookingEditDto::class,
|
||||||
'anti_xss' => true,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,8 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Form\ChoiceLoader;
|
namespace App\Form\ChoiceLoader;
|
||||||
|
|
||||||
use App\Form\DataAdapters\ParticipantDataAdapter;
|
use App\Form\Model\ParticipantDto;
|
||||||
|
use App\Form\Model\RoomSelectionDto;
|
||||||
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
|
use Symfony\Component\Form\ChoiceList\ChoiceListInterface;
|
||||||
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
|
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
|
||||||
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
|
use Symfony\Component\Form\ChoiceList\Loader\ChoiceLoaderInterface;
|
||||||
@@ -21,9 +22,14 @@ class ParticipantRoomChoiceLoader implements ChoiceLoaderInterface
|
|||||||
{
|
{
|
||||||
private ?ChoiceListInterface $choiceList = null;
|
private ?ChoiceListInterface $choiceList = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param ParticipantDto[] $allParticipants all participant DTOs from the root form
|
||||||
|
* @param RoomSelectionDto[] $selectedRooms the rooms selected in the previous step
|
||||||
|
* @param int $participantIndex the index of the current participant
|
||||||
|
*/
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly ChoiceListFactoryInterface $factory,
|
private readonly ChoiceListFactoryInterface $factory,
|
||||||
private readonly ParticipantDataAdapter $adapter,
|
private readonly array $allParticipants,
|
||||||
private readonly array $selectedRooms,
|
private readonly array $selectedRooms,
|
||||||
private readonly int $participantIndex,
|
private readonly int $participantIndex,
|
||||||
) {
|
) {
|
||||||
@@ -66,7 +72,7 @@ class ParticipantRoomChoiceLoader implements ChoiceLoaderInterface
|
|||||||
{
|
{
|
||||||
$roomOccupancy = $this->calculateRoomOccupancy();
|
$roomOccupancy = $this->calculateRoomOccupancy();
|
||||||
$participantRoomChoices = [];
|
$participantRoomChoices = [];
|
||||||
$assignedRoomId = $this->getParticipantAssignedRoomId();
|
$assignedRoomId = $this->allParticipants[$this->participantIndex]->assignedRoomId ?? null;
|
||||||
|
|
||||||
foreach ($this->selectedRooms as $roomSelection) {
|
foreach ($this->selectedRooms as $roomSelection) {
|
||||||
$currentOccupancy = $roomOccupancy[$roomSelection->roomId] ?? 0;
|
$currentOccupancy = $roomOccupancy[$roomSelection->roomId] ?? 0;
|
||||||
@@ -77,7 +83,8 @@ class ParticipantRoomChoiceLoader implements ChoiceLoaderInterface
|
|||||||
--$adjustedOccupancy;
|
--$adjustedOccupancy;
|
||||||
}
|
}
|
||||||
|
|
||||||
$remainingCapacity = $roomSelection->minPax - $adjustedOccupancy;
|
$totalAvailableRooms = $roomSelection->quantity;
|
||||||
|
$remainingCapacity = $totalAvailableRooms - $adjustedOccupancy;
|
||||||
|
|
||||||
// Include room if it has capacity OR if it's the participant's current assignment
|
// Include room if it has capacity OR if it's the participant's current assignment
|
||||||
if ($remainingCapacity > 0 || $assignedRoomId === $roomSelection->roomId) {
|
if ($remainingCapacity > 0 || $assignedRoomId === $roomSelection->roomId) {
|
||||||
@@ -89,7 +96,7 @@ class ParticipantRoomChoiceLoader implements ChoiceLoaderInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates room occupancy based on participant assignments.
|
* Calculates room occupancy based on all participant assignments.
|
||||||
*
|
*
|
||||||
* @return array<int, int>
|
* @return array<int, int>
|
||||||
*/
|
*/
|
||||||
@@ -97,24 +104,12 @@ class ParticipantRoomChoiceLoader implements ChoiceLoaderInterface
|
|||||||
{
|
{
|
||||||
$roomOccupancy = [];
|
$roomOccupancy = [];
|
||||||
|
|
||||||
foreach ($this->adapter->getParticipants() as $participant) {
|
foreach ($this->allParticipants as $participant) {
|
||||||
$assignedRoomId = $this->adapter->getAssignedRoomId($participant);
|
if (null !== $participant->assignedRoomId) {
|
||||||
if (null !== $assignedRoomId) {
|
$roomOccupancy[$participant->assignedRoomId] = ($roomOccupancy[$participant->assignedRoomId] ?? 0) + 1;
|
||||||
$roomOccupancy[$assignedRoomId] = ($roomOccupancy[$assignedRoomId] ?? 0) + 1;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return $roomOccupancy;
|
return $roomOccupancy;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the assigned room ID for the current participant.
|
|
||||||
*/
|
|
||||||
private function getParticipantAssignedRoomId(): ?int
|
|
||||||
{
|
|
||||||
$participants = $this->adapter->getParticipants();
|
|
||||||
$participant = $participants[$this->participantIndex] ?? null;
|
|
||||||
|
|
||||||
return null !== $participant ? $this->adapter->getAssignedRoomId($participant) : null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
declare(strict_types=1);
|
|
||||||
|
|
||||||
namespace App\Form\DataAdapters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adapter pattern to unify access to participant data regardless of whether it comes from
|
|
||||||
* objects (during PRE_SET_DATA) or arrays (during PRE_SUBMIT).
|
|
||||||
*/
|
|
||||||
class ParticipantDataAdapter
|
|
||||||
{
|
|
||||||
private array $participants;
|
|
||||||
|
|
||||||
public function __construct(array $participants)
|
|
||||||
{
|
|
||||||
$this->participants = $participants;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getParticipants(): array
|
|
||||||
{
|
|
||||||
return $this->participants;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getAssignedRoomId(mixed $participant): ?int
|
|
||||||
{
|
|
||||||
if (is_array($participant)) {
|
|
||||||
$assignedRoomId = $participant['assignedRoomId'] ?? null;
|
|
||||||
return null !== $assignedRoomId ? (int) $assignedRoomId : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $participant->assignedRoomId;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\DataTransformer;
|
||||||
|
|
||||||
|
use Symfony\Component\Form\DataTransformerInterface;
|
||||||
|
use voku\helper\AntiXSS;
|
||||||
|
|
||||||
|
class XssCleanTransformer implements DataTransformerInterface
|
||||||
|
{
|
||||||
|
private ?AntiXSS $antiXss = null;
|
||||||
|
|
||||||
|
public function transform(mixed $value): mixed
|
||||||
|
{
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reverseTransform(mixed $value): mixed
|
||||||
|
{
|
||||||
|
if (false === is_string($value) || true === empty($value)) {
|
||||||
|
return $value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $this->antiXss) {
|
||||||
|
$this->antiXss = new AntiXSS();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->antiXss->xss_clean($value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,45 +0,0 @@
|
|||||||
<?php
|
|
||||||
|
|
||||||
namespace App\Form\Extension;
|
|
||||||
|
|
||||||
use Symfony\Component\Form\AbstractTypeExtension;
|
|
||||||
use Symfony\Component\Form\Extension\Core\Type\FormType;
|
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
|
||||||
use Symfony\Component\Form\FormEvent;
|
|
||||||
use Symfony\Component\Form\FormEvents;
|
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
|
||||||
use voku\helper\AntiXSS;
|
|
||||||
|
|
||||||
class AntiXssExtension extends AbstractTypeExtension
|
|
||||||
{
|
|
||||||
public function buildForm(FormBuilderInterface $builder, array $options): void
|
|
||||||
{
|
|
||||||
if (false === $options['anti_xss']) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
|
|
||||||
$data = $event->getData();
|
|
||||||
if (null === $data) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$antiXss = new AntiXSS();
|
|
||||||
foreach ($data as $key => $value) {
|
|
||||||
$data[$key] = $antiXss->xss_clean($value);
|
|
||||||
}
|
|
||||||
$event->setData($data);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
|
||||||
{
|
|
||||||
$resolver->setDefaults([
|
|
||||||
'anti_xss' => false,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static function getExtendedTypes(): iterable
|
|
||||||
{
|
|
||||||
return [FormType::class];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Form\Extension;
|
||||||
|
|
||||||
|
use App\Form\DataTransformer\XssCleanTransformer;
|
||||||
|
use Symfony\Component\Form\AbstractTypeExtension;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
|
||||||
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
|
use Symfony\Component\OptionsResolver\OptionsResolver;
|
||||||
|
|
||||||
|
class XssCleanExtension extends AbstractTypeExtension
|
||||||
|
{
|
||||||
|
public function buildForm(FormBuilderInterface $builder, array $options): void
|
||||||
|
{
|
||||||
|
if (true === $options['clean_xss']) {
|
||||||
|
$builder->addModelTransformer(new XssCleanTransformer());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configureOptions(OptionsResolver $resolver): void
|
||||||
|
{
|
||||||
|
$resolver->setDefaults([
|
||||||
|
'clean_xss' => false,
|
||||||
|
]);
|
||||||
|
$resolver->setAllowedTypes('clean_xss', 'bool');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getExtendedTypes(): iterable
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
TextType::class,
|
||||||
|
TextareaType::class,
|
||||||
|
EmailType::class,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,6 @@ use Symfony\Component\Form\AbstractType;
|
|||||||
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
use Symfony\Component\Form\Extension\Core\Type\EmailType;
|
||||||
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
use Symfony\Component\Form\Extension\Core\Type\TextType;
|
||||||
use Symfony\Component\Form\FormBuilderInterface;
|
use Symfony\Component\Form\FormBuilderInterface;
|
||||||
use Symfony\Component\OptionsResolver\OptionsResolver;
|
|
||||||
|
|
||||||
class PersonalDataType extends AbstractType
|
class PersonalDataType extends AbstractType
|
||||||
{
|
{
|
||||||
@@ -17,14 +16,17 @@ class PersonalDataType extends AbstractType
|
|||||||
->add('street', TextType::class, [
|
->add('street', TextType::class, [
|
||||||
'label' => 'Straße',
|
'label' => 'Straße',
|
||||||
'property_path' => 'address.street',
|
'property_path' => 'address.street',
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('postCode', TextType::class, [
|
->add('postCode', TextType::class, [
|
||||||
'label' => 'PLZ',
|
'label' => 'PLZ',
|
||||||
'property_path' => 'address.postCode',
|
'property_path' => 'address.postCode',
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('city', TextType::class, [
|
->add('city', TextType::class, [
|
||||||
'label' => 'Stadt',
|
'label' => 'Stadt',
|
||||||
'property_path' => 'address.city',
|
'property_path' => 'address.city',
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('country', CountryType::class, [
|
->add('country', CountryType::class, [
|
||||||
'label' => 'Land',
|
'label' => 'Land',
|
||||||
@@ -33,23 +35,19 @@ class PersonalDataType extends AbstractType
|
|||||||
->add('email', EmailType::class, [
|
->add('email', EmailType::class, [
|
||||||
'label' => 'E-Mail',
|
'label' => 'E-Mail',
|
||||||
'property_path' => 'communication.email',
|
'property_path' => 'communication.email',
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('phone', TextType::class, [
|
->add('phone', TextType::class, [
|
||||||
'label' => 'Telefon',
|
'label' => 'Telefon',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'property_path' => 'communication.phone',
|
'property_path' => 'communication.phone',
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('mobile', TextType::class, [
|
->add('mobile', TextType::class, [
|
||||||
'label' => 'Mobil',
|
'label' => 'Mobil',
|
||||||
'property_path' => 'communication.mobile',
|
'property_path' => 'communication.mobile',
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function configureOptions(OptionsResolver $resolver): void
|
|
||||||
{
|
|
||||||
$resolver->setDefaults([
|
|
||||||
'anti_xss' => true,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,13 +25,14 @@ class RegistrationType extends AbstractType
|
|||||||
])
|
])
|
||||||
->add('firstName', TextType::class, [
|
->add('firstName', TextType::class, [
|
||||||
'label' => 'Name',
|
'label' => 'Name',
|
||||||
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
->add('name', TextType::class, [
|
->add('name', TextType::class, [
|
||||||
'label' => 'Nachname',
|
'label' => 'Nachname',
|
||||||
])
|
])
|
||||||
->add('email', EmailType::class, [
|
->add('email', EmailType::class, [
|
||||||
'label' => 'E-Mail',
|
'label' => 'E-Mail',
|
||||||
'property_path' => 'email',
|
'anti_xss' => true,
|
||||||
])
|
])
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
@@ -40,7 +41,6 @@ class RegistrationType extends AbstractType
|
|||||||
{
|
{
|
||||||
$resolver->setDefaults([
|
$resolver->setDefaults([
|
||||||
'data_class' => RegistrationDto::class,
|
'data_class' => RegistrationDto::class,
|
||||||
'anti_xss' => true,
|
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,14 +5,12 @@ declare(strict_types=1);
|
|||||||
namespace App\Form\Service;
|
namespace App\Form\Service;
|
||||||
|
|
||||||
use App\Form\ChoiceLoader\ParticipantRoomChoiceLoader;
|
use App\Form\ChoiceLoader\ParticipantRoomChoiceLoader;
|
||||||
use App\Form\DataAdapters\ParticipantDataAdapter;
|
use App\Form\Model\ParticipantDto;
|
||||||
|
use App\Form\Model\RoomSelectionDto;
|
||||||
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
|
use Symfony\Component\Form\ChoiceList\Factory\ChoiceListFactoryInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Factory service for creating ParticipantRoomChoiceLoader instances.
|
* Factory service for creating ParticipantRoomChoiceLoader instances.
|
||||||
*
|
|
||||||
* This service provides a clean way to create choice loaders with proper
|
|
||||||
* dependency injection instead of manually instantiating factories in form types.
|
|
||||||
*/
|
*/
|
||||||
class ParticipantRoomChoiceLoaderFactory
|
class ParticipantRoomChoiceLoaderFactory
|
||||||
{
|
{
|
||||||
@@ -23,15 +21,18 @@ class ParticipantRoomChoiceLoaderFactory
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a ParticipantRoomChoiceLoader for the given participant and room data.
|
* Creates a ParticipantRoomChoiceLoader for the given participant and room data.
|
||||||
|
*
|
||||||
|
* @param ParticipantDto[] $allParticipants
|
||||||
|
* @param RoomSelectionDto[] $selectedRooms
|
||||||
*/
|
*/
|
||||||
public function createChoiceLoader(
|
public function create(
|
||||||
ParticipantDataAdapter $adapter,
|
array $allParticipants,
|
||||||
array $selectedRooms,
|
array $selectedRooms,
|
||||||
int $participantIndex,
|
int $participantIndex,
|
||||||
): ParticipantRoomChoiceLoader {
|
): ParticipantRoomChoiceLoader {
|
||||||
return new ParticipantRoomChoiceLoader(
|
return new ParticipantRoomChoiceLoader(
|
||||||
$this->choiceListFactory,
|
$this->choiceListFactory,
|
||||||
$adapter,
|
$allParticipants,
|
||||||
$selectedRooms,
|
$selectedRooms,
|
||||||
$participantIndex
|
$participantIndex
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user