feat: replace dropdowns with individual fields for body dimensions

This commit is contained in:
Björn Fromme
2026-05-28 13:59:30 +02:00
parent fa7e5c2091
commit bdf8cffd04
8 changed files with 366 additions and 148 deletions
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace App\Validator\Constraints;
use App\Form\Model\ParticipantDto;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;
@@ -12,10 +13,24 @@ use Symfony\Component\Validator\ConstraintValidator;
* Validates cross-field constraints on participant data.
*
* Enforces business rules that require examining multiple fields together,
* such as requiring a pickup location when bus transportation is selected.
* such as requiring a pickup location when bus transportation is selected,
* and validates body dimension ranges when values are provided.
*/
class ParticipantValidator extends ConstraintValidator
{
private const RANGE_MESSAGE = 'Bitte gib eine Zahl zwischen %d und %d ein. Falls du außerhalb dieses Bereichs bist, ruf gerne unser Kundenoffice an.';
/** @var array<string, int> */
private array $bodyDimensionRanges;
/**
* @param array<string, int> $bodyDimensionRanges
*/
public function __construct(array $bodyDimensionRanges = [])
{
$this->bodyDimensionRanges = $this->resolveBodyDimensionRanges($bodyDimensionRanges);
}
public function validate(mixed $value, Constraint $constraint): void
{
/** @var ParticipantDto $participant */
@@ -24,6 +39,7 @@ class ParticipantValidator extends ConstraintValidator
$this->assertPickupSelected($participant);
$this->assertDropOffSelected($participant);
$this->assertBodyDimensionsWhenRentalsSelected($participant);
$this->assertBodyDimensionsInRange($participant);
}
public function assertPickupSelected(ParticipantDto $participant): void
@@ -93,4 +109,71 @@ class ParticipantValidator extends ConstraintValidator
;
}
}
private function assertBodyDimensionsInRange(ParticipantDto $participant): void
{
$this->assertInRange(
$participant->height,
'height',
$this->bodyDimensionRanges['height_min'],
$this->bodyDimensionRanges['height_max']
);
$this->assertInRange(
$participant->weight,
'weight',
$this->bodyDimensionRanges['weight_min'],
$this->bodyDimensionRanges['weight_max']
);
$this->assertInRange(
$participant->shoeSize,
'shoeSize',
$this->bodyDimensionRanges['shoe_size_min'],
$this->bodyDimensionRanges['shoe_size_max']
);
}
private function assertInRange(int|string|null $value, string $path, int $min, int $max): void
{
if (null === $value || '' === $value) {
return;
}
$intValue = (int) $value;
if ($min > $intValue || $max < $intValue) {
$this->context->buildViolation(sprintf(self::RANGE_MESSAGE, $min, $max))
->atPath($path)
->addViolation();
}
}
/**
* @param array<string, int> $options
*
* @return array<string, int>
*/
private function resolveBodyDimensionRanges(array $options): array
{
$resolver = new OptionsResolver();
$resolver->setDefaults([
'height_min' => 145,
'height_max' => 200,
'weight_min' => 40,
'weight_max' => 120,
'shoe_size_min' => 35,
'shoe_size_max' => 50,
]);
$resolver->setAllowedTypes('height_min', 'int');
$resolver->setAllowedTypes('height_max', 'int');
$resolver->setAllowedTypes('weight_min', 'int');
$resolver->setAllowedTypes('weight_max', 'int');
$resolver->setAllowedTypes('shoe_size_min', 'int');
$resolver->setAllowedTypes('shoe_size_max', 'int');
return $resolver->resolve($options);
}
}