wip: email uniqueness validation
This commit is contained in:
@@ -1,138 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service;
|
||||
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Abstract\AbstractParticipantFieldHandler;
|
||||
|
||||
/**
|
||||
* Handles email uniqueness validation for booking participants.
|
||||
*
|
||||
* This handler provides real-time feedback during form refresh when an adult
|
||||
* participant enters an email address that is already used by another adult
|
||||
* participant in the same booking. Children (under 16) are exempt from this
|
||||
* validation and can share email addresses with adults or other children.
|
||||
*
|
||||
* The handler adds warning notifications during the HTMX refresh cycle,
|
||||
* providing immediate user feedback without blocking form submission.
|
||||
* Hard validation is enforced via BookingDto::validateEmailUniqueness()
|
||||
* when the user attempts to proceed to the next step.
|
||||
*/
|
||||
class ParticipantEmailFieldHandler extends AbstractParticipantFieldHandler
|
||||
{
|
||||
/**
|
||||
* Returns the form field name this handler processes.
|
||||
*
|
||||
* @return string The field name 'email'
|
||||
*/
|
||||
public function getFieldName(): string
|
||||
{
|
||||
return 'email';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field dependencies for proper processing order.
|
||||
*
|
||||
* Email validation has no dependencies on other fields.
|
||||
*
|
||||
* @return string[] Empty array (no dependencies)
|
||||
*/
|
||||
public function getDependencies(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if this handler should process the field based on submitted data.
|
||||
*
|
||||
* Email validation should always run when the email field is present in
|
||||
* the submitted data, regardless of booking mode or participant index.
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param string $mode The booking mode (BookingDto::MODE_CREATE or MODE_EDIT)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*
|
||||
* @return bool True if email field exists in submitted data
|
||||
*/
|
||||
public function shouldProcess(array $submittedData, string $mode, int $participantIndex): bool
|
||||
{
|
||||
return array_key_exists($this->getFieldName(), $submittedData);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes the email field for a specific participant.
|
||||
*
|
||||
* This method checks if the participant's email address is already used by
|
||||
* another adult participant in the same booking. If a duplicate is found,
|
||||
* a warning notification is added to the participant for display as a toast.
|
||||
*
|
||||
* Validation rules:
|
||||
* - Children (under 16 at current date) are exempt from uniqueness validation
|
||||
* - Null or empty emails are skipped (handled by Symfony's @Assert\Email)
|
||||
* - Only duplicates with other adult participants trigger warnings
|
||||
*
|
||||
* @param array<string, mixed> $submittedData The submitted participant form data
|
||||
* @param BookingDto $bookingDto The booking DTO to update (create or edit)
|
||||
* @param int $participantIndex The index of the participant being processed
|
||||
*/
|
||||
public function processField(array $submittedData, BookingDto $bookingDto, int $participantIndex): void
|
||||
{
|
||||
// Safely get the participant object, returning early if not found
|
||||
$participant = $this->getParticipant($bookingDto, $participantIndex);
|
||||
if (null === $participant) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip validation for children (they can share email addresses)
|
||||
if (true === $participant->isChild()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the email value from submitted data (field handlers run in PRE_SUBMIT, before form binding)
|
||||
$email = $submittedData[$this->getFieldName()] ?? null;
|
||||
|
||||
// Skip if email is null or empty (handled by other validators)
|
||||
if (null === $email || '' === trim($email)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize email for comparison (case-insensitive)
|
||||
$normalizedEmail = strtolower(trim($email));
|
||||
|
||||
// Check for duplicate emails among other adult participants
|
||||
$hasDuplicate = false;
|
||||
foreach ($bookingDto->participants as $index => $otherParticipant) {
|
||||
// Skip comparing with self
|
||||
if ($index === $participantIndex) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if other participant is a child (children can share emails)
|
||||
if (true === $otherParticipant->isChild()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip if other participant has no email
|
||||
if (null === $otherParticipant->email || '' === trim($otherParticipant->email)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compare normalized emails
|
||||
$otherNormalizedEmail = strtolower(trim($otherParticipant->email));
|
||||
if ($normalizedEmail === $otherNormalizedEmail) {
|
||||
$hasDuplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Add warning notification if duplicate found
|
||||
if (true === $hasDuplicate) {
|
||||
$participant->addNotification(
|
||||
'warning',
|
||||
'Diese E-Mail Adresse wird bereits von einem anderen erwachsenen Teilnehmer verwendet'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user