feat: improved dynamic validation

This commit is contained in:
Björn Fromme
2025-10-31 16:23:06 +01:00
parent a619add4e3
commit 29f415a91f
11 changed files with 143 additions and 292 deletions
+60 -2
View File
@@ -9,9 +9,9 @@ use App\BusProNet\Model\Pickup;
use App\BusProNet\Model\Service;
use App\Validator\Constraints as AppAssert;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Component\Validator\Context\ExecutionContextInterface;
#[AppAssert\Participant(groups: ['booking_edit', 'booking_create'])]
#[AppAssert\Booking(groups: ['booking_edit', 'booking_create'])]
class ParticipantDto
{
/**
@@ -42,8 +42,10 @@ class ParticipantDto
public bool $mutable = false;
public bool $touched = false;
#[Assert\NotBlank(message: 'Bitte angeben', groups: ['strict_required'])]
public ?string $firstName = null;
#[Assert\NotBlank(message: 'Bitte angeben', groups: ['strict_required'])]
public ?string $lastName = null;
public ?string $title = null;
public ?string $gender = null;
@@ -53,9 +55,11 @@ class ParticipantDto
public ?string $shoeSize = null;
public ?string $weight = null;
#[Assert\NotNull(message: 'Bitte angeben', groups: ['strict_required'])]
public ?\DateTimeImmutable $dateOfBirth = null;
#[Assert\Email(message: 'Bitte eine gültige E-Mail Adresse angeben', mode: 'strict', groups: ['booking_edit', 'booking_create'])]
#[Assert\NotBlank(message: 'Bitte angeben', groups: ['strict_required'])]
public ?string $email = null;
public ?string $mobile = null;
@@ -64,7 +68,7 @@ class ParticipantDto
#[Assert\NotNull(message: 'Bitte Adresse angeben', groups: ['applicant_address'])]
public ?Address $address = null;
#[Assert\NotNull(message: 'Bitte ein Zimmer auswählen', groups: ['booking_create'])]
#[Assert\NotNull(message: 'Bitte ein Zimmer auswählen', groups: ['strict_required'])]
public ?int $assignedRoomId = null;
public ?string $remarksRoom = null;
@@ -72,6 +76,7 @@ class ParticipantDto
public array $courses = [];
public array $additionalServices = [];
#[Assert\NotNull(message: 'Bitte auswählen', groups: ['strict_required'])]
public ?Service $skiPass = null;
public array $board = [];
@@ -82,7 +87,9 @@ class ParticipantDto
public bool $rentalInsuranceSelected = false;
// Transportation services with improved naming (outbound/inbound)
#[Assert\NotNull(message: 'Bitte auswählen', groups: ['strict_required'])]
public ?Service $transportationOutbound = null;
#[Assert\NotNull(message: 'Bitte auswählen', groups: ['strict_required'])]
public ?Service $transportationInbound = null;
// Pickup location (applies to both directions)
@@ -98,6 +105,7 @@ class ParticipantDto
public ?string $licensePlate = null;
// Selected insurance for this participant (individual insurance selection per participant)
#[Assert\NotNull(message: 'Bitte auswählen', groups: ['strict_required'])]
public ?Insurance $insurance = null;
// Bulk insurance booking flag (applicant only: when checked, assigns same insurance type to all participants)
@@ -252,4 +260,54 @@ class ParticipantDto
return $ageThreshold > $age;
}
/**
* Validates that the applicant (index 0) has a complete address.
*
* This callback only applies to the applicant participant. Address validation includes:
* - Address object must exist
* - All required address fields must be filled (street, postCode, city, country)
*/
#[Assert\Callback(groups: ['strict_required'])]
public function validateApplicantAddress(ExecutionContextInterface $context): void
{
// Only validate applicant's address
if (0 !== $this->index) {
return;
}
// Address object is required for applicant
if (null === $this->address) {
$context->buildViolation('Bitte angeben')
->atPath('address')
->addViolation();
return;
}
// Validate address subfields
if (null === $this->address->street || '' === trim($this->address->street)) {
$context->buildViolation('Bitte angeben')
->atPath('address.street')
->addViolation();
}
if (null === $this->address->postCode || '' === trim($this->address->postCode)) {
$context->buildViolation('Bitte angeben')
->atPath('address.postCode')
->addViolation();
}
if (null === $this->address->city || '' === trim($this->address->city)) {
$context->buildViolation('Bitte angeben')
->atPath('address.city')
->addViolation();
}
if (null === $this->address->country || '' === trim($this->address->country)) {
$context->buildViolation('Bitte angeben')
->atPath('address.country')
->addViolation();
}
}
}
-1
View File
@@ -16,7 +16,6 @@ use Symfony\Component\Validator\Context\ExecutionContextInterface;
* the participant being edited and the full booking context needed for
* cross-participant validation.
*/
#[AppAssert\Booking(groups: ['booking_edit', 'booking_create'])]
class ParticipantEditDto
{
public function __construct(