633 lines
25 KiB
PHP
633 lines
25 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
namespace App\Form\Service;
|
|
|
|
use App\BusProNet\Model\Address;
|
|
use App\BusProNet\Model\Insurance;
|
|
use App\BusProNet\Model\Pickup;
|
|
use App\BusProNet\Model\Service;
|
|
use App\Form\Model\BookingDto;
|
|
use App\Form\Model\ParticipantDto;
|
|
use App\Form\Service\Contract\ParticipantFieldHandlerInterface;
|
|
|
|
/**
|
|
* Registry for managing and executing participant field handlers in dependency order.
|
|
*
|
|
* This class ensures that participant field handlers are executed in the correct order
|
|
* based on their dependencies, preventing issues where a field depends on
|
|
* another field that hasn't been processed yet.
|
|
*/
|
|
class ParticipantFieldHandlerRegistry
|
|
{
|
|
private const MUTABILITY_ADDITIONAL_SERVICES = 'additional_services';
|
|
private const MUTABILITY_TRANSPORTATION = 'transportation';
|
|
private const MUTABILITY_PICKUPS = 'pickups';
|
|
|
|
/**
|
|
* Maps handler names to mutability categories for edit-mode enforcement.
|
|
*
|
|
* @var array<string, string>
|
|
*/
|
|
private const HANDLER_MUTABILITY_CATEGORY_MAP = [
|
|
'additionalServices' => self::MUTABILITY_ADDITIONAL_SERVICES,
|
|
'courses' => self::MUTABILITY_ADDITIONAL_SERVICES,
|
|
'board' => self::MUTABILITY_ADDITIONAL_SERVICES,
|
|
'veg' => self::MUTABILITY_ADDITIONAL_SERVICES,
|
|
'skiPass' => self::MUTABILITY_ADDITIONAL_SERVICES,
|
|
'rentals' => self::MUTABILITY_ADDITIONAL_SERVICES,
|
|
'rentalInsurance' => self::MUTABILITY_ADDITIONAL_SERVICES,
|
|
'transportationOutbound' => self::MUTABILITY_TRANSPORTATION,
|
|
'transportationInbound' => self::MUTABILITY_TRANSPORTATION,
|
|
'parking' => self::MUTABILITY_TRANSPORTATION,
|
|
'licensePlate' => self::MUTABILITY_TRANSPORTATION,
|
|
'transportationDiscountReplacement' => self::MUTABILITY_TRANSPORTATION,
|
|
'pickup' => self::MUTABILITY_PICKUPS,
|
|
'dropOff' => self::MUTABILITY_PICKUPS,
|
|
];
|
|
|
|
/** @var array<string, ParticipantFieldHandlerInterface> Registered handlers indexed by field name */
|
|
private array $handlers = [];
|
|
|
|
/** @var string[]|null Cached array of handler names sorted by dependency order */
|
|
private ?array $sortedHandlers = null;
|
|
|
|
/**
|
|
* Initializes the registry with a hybrid array of handlers.
|
|
*
|
|
* This constructor supports both simple handlers (passed as class names) and
|
|
* complex handlers (passed as instantiated service objects). Simple handlers
|
|
* with no dependencies can be passed as strings and will be instantiated
|
|
* automatically. Complex handlers with dependencies should be passed as
|
|
* already-instantiated objects via dependency injection.
|
|
*
|
|
* @param array<ParticipantFieldHandlerInterface|string> $handlers Array of handler instances or class names
|
|
*
|
|
* @throws \Exception If a class name cannot be instantiated
|
|
*/
|
|
public function __construct(array $handlers)
|
|
{
|
|
foreach ($handlers as $handler) {
|
|
if (is_string($handler)) {
|
|
// It's a class name - instantiate it (for simple handlers without dependencies)
|
|
$handler = new $handler();
|
|
}
|
|
// If it's already an object (service), use as-is (for complex handlers with dependencies)
|
|
$this->addHandler($handler);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registers a participant field handler with the registry.
|
|
*
|
|
* Handlers are indexed by their field name to ensure uniqueness and enable
|
|
* fast lookups. Adding a handler invalidates the dependency sort cache,
|
|
* forcing a re-sort on the next processing request.
|
|
*
|
|
* @param ParticipantFieldHandlerInterface $handler The handler to register
|
|
*/
|
|
public function addHandler(ParticipantFieldHandlerInterface $handler): void
|
|
{
|
|
$this->handlers[$handler->getFieldName()] = $handler;
|
|
$this->sortedHandlers = null; // Reset cache to force re-sorting with new handler
|
|
}
|
|
|
|
/**
|
|
* Processes all participant fields and synchronizes submitted data with cleaned DTO state.
|
|
*
|
|
* This method combines field processing with data synchronization to ensure that
|
|
* the submitted form data reflects any changes made by field handlers. This is
|
|
* particularly useful for HTMX form updates where invalid selections need to be
|
|
* automatically cleared.
|
|
*
|
|
* @param array<string, mixed> $submittedData The submitted form data containing participants array
|
|
* @param BookingDto $bookingDto The booking DTO to update with processed field values
|
|
*
|
|
* @return array<string, mixed> The synchronized submitted data reflecting DTO changes
|
|
*/
|
|
public function processFieldsAndSync(array $submittedData, BookingDto $bookingDto): array
|
|
{
|
|
// Process all field handlers to clean the DTO
|
|
$this->processFields($submittedData, $bookingDto);
|
|
|
|
// Synchronize submitted data with the cleaned DTO state
|
|
return $this->syncSubmittedDataWithDto($submittedData, $bookingDto);
|
|
}
|
|
|
|
/**
|
|
* Processes all participant fields from submitted form data using registered handlers.
|
|
*
|
|
* This is the main entry point for field processing. It processes handlers in dependency
|
|
* order, applying each handler to ALL participants before moving to the next handler.
|
|
* This ensures that cross-participant logic (like family booking detection) has access
|
|
* to complete data from all participants.
|
|
*
|
|
* Processing order: handler-first, then participants
|
|
* - Process handler A for all participants
|
|
* - Process handler B for all participants
|
|
* - etc.
|
|
*
|
|
* This is critical for handlers that depend on booking-level state (like insurance
|
|
* family detection which needs all participants' ages to be processed first).
|
|
*
|
|
* @param array<string, mixed> $submittedData The submitted form data containing participants array
|
|
* @param BookingDto $bookingDto The booking DTO to update with processed field values (create or edit)
|
|
*/
|
|
public function processFields(array $submittedData, BookingDto $bookingDto): void
|
|
{
|
|
// Early return if no participant data exists in submission
|
|
if (false === isset($submittedData['participants']) || false === is_array($submittedData['participants'])) {
|
|
return;
|
|
}
|
|
|
|
// Get handlers sorted by dependency order (uses cache if available)
|
|
$sortedHandlerNames = $this->getSortedHandlers();
|
|
|
|
// Process each handler across all participants before moving to the next handler
|
|
// This ensures booking-level state (like family booking detection) is accurate
|
|
foreach ($sortedHandlerNames as $handlerName) {
|
|
if (false === $this->isHandlerMutableForBooking($handlerName, $bookingDto)) {
|
|
continue;
|
|
}
|
|
|
|
$handler = $this->handlers[$handlerName];
|
|
|
|
// Apply this handler to all participants
|
|
foreach ($submittedData['participants'] as $participantIndex => $participantData) {
|
|
// Skip invalid participant data
|
|
if (false === is_array($participantData)) {
|
|
continue;
|
|
}
|
|
|
|
// Let each handler decide if it should process this participant's data
|
|
if ($handler->shouldProcess($participantData, $bookingDto->getMode(), (int) $participantIndex)) {
|
|
$handler->processField($participantData, $bookingDto, (int) $participantIndex);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes field handlers for a single participant.
|
|
*
|
|
* This method is used by the card-based booking flows where participants are
|
|
* edited individually. Unlike processFields() which processes all participants,
|
|
* this method applies all handlers to only the specified participant.
|
|
*
|
|
* Handlers are executed in dependency order to ensure proper data consistency.
|
|
*
|
|
* @param array<string, mixed> $participantData Submitted data for one participant
|
|
* @param BookingDto $bookingDto The booking DTO to update
|
|
* @param int $participantIndex Index of participant to process
|
|
*/
|
|
public function processFieldsForParticipant(array $participantData, BookingDto $bookingDto, int $participantIndex): void
|
|
{
|
|
// Get handlers sorted by dependency order (uses cache if available)
|
|
$sortedHandlerNames = $this->getSortedHandlers();
|
|
|
|
// Apply each handler to the specified participant
|
|
foreach ($sortedHandlerNames as $handlerName) {
|
|
if (false === $this->isHandlerMutableForBooking($handlerName, $bookingDto)) {
|
|
continue;
|
|
}
|
|
|
|
$handler = $this->handlers[$handlerName];
|
|
|
|
// Let each handler decide if it should process this participant's data
|
|
if ($handler->shouldProcess($participantData, $bookingDto->getMode(), $participantIndex)) {
|
|
$handler->processField($participantData, $bookingDto, $participantIndex);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes field handlers for a single participant and synchronizes submitted data.
|
|
*
|
|
* This method combines field processing with data synchronization to ensure that
|
|
* the submitted form data reflects any changes made by field handlers. This is
|
|
* essential for handlers that modify fields (like discount replacement) to have
|
|
* their changes reflected in the form binding.
|
|
*
|
|
* Only fields that are actually modified by handlers (detected by comparing
|
|
* submitted ID with DTO value after processing) are synchronized. This prevents
|
|
* overwriting partial user input (like incomplete dates).
|
|
*
|
|
* @param array<string, mixed> $participantData Submitted data for one participant
|
|
* @param BookingDto $bookingDto The booking DTO to update
|
|
* @param int $participantIndex Index of participant to process
|
|
*
|
|
* @return array<string, mixed> The synchronized participant data reflecting DTO changes
|
|
*/
|
|
public function processFieldsForParticipantAndSync(array $participantData, BookingDto $bookingDto, int $participantIndex): array
|
|
{
|
|
// Process all field handlers for this participant
|
|
$this->processFieldsForParticipant($participantData, $bookingDto, $participantIndex);
|
|
|
|
// Synchronize only modified service fields with the cleaned DTO state
|
|
$participant = $bookingDto->getParticipant($participantIndex);
|
|
if (null === $participant) {
|
|
return $participantData;
|
|
}
|
|
|
|
return $this->syncModifiedServiceFields($participantData, $participant);
|
|
}
|
|
|
|
/**
|
|
* Synchronizes only service fields that were modified by handlers.
|
|
*
|
|
* This method compares the submitted service ID with the DTO value after processing.
|
|
* Only fields where the handler assigned a different service are synchronized back.
|
|
* This prevents overwriting user input for fields like dateOfBirth where the handler
|
|
* just parses the value without changing it.
|
|
*
|
|
* @param array<string, mixed> $participantData The submitted participant data
|
|
* @param ParticipantDto $participant The processed participant DTO
|
|
*
|
|
* @return array<string, mixed> Updated participant data with modified fields synchronized
|
|
*/
|
|
private function syncModifiedServiceFields(array $participantData, ParticipantDto $participant): array
|
|
{
|
|
// Define fields that may be reassigned by handlers and need syncing
|
|
$serviceFields = [
|
|
'transportationOutbound',
|
|
'transportationInbound',
|
|
'insurance',
|
|
'skiPass',
|
|
'rentals',
|
|
'courses',
|
|
'additionalServices',
|
|
'board',
|
|
'veg',
|
|
'pickup',
|
|
'dropOff',
|
|
'parking',
|
|
'licensePlate',
|
|
];
|
|
|
|
foreach ($serviceFields as $fieldName) {
|
|
if (false === array_key_exists($fieldName, $participantData)) {
|
|
continue;
|
|
}
|
|
|
|
if (false === property_exists($participant, $fieldName)) {
|
|
continue;
|
|
}
|
|
|
|
$dtoValue = $participant->{$fieldName};
|
|
$submittedValue = $participantData[$fieldName];
|
|
|
|
// Check if the DTO value differs from submitted value
|
|
if ($this->hasFieldValueChanged($submittedValue, $dtoValue)) {
|
|
$participantData[$fieldName] = $this->convertDtoValueToSubmittedFormat($dtoValue);
|
|
}
|
|
}
|
|
|
|
return $participantData;
|
|
}
|
|
|
|
/**
|
|
* Checks if a field value was changed by a handler.
|
|
*
|
|
* Compares the submitted value (usually an ID string) with the DTO value
|
|
* (usually a Service/Insurance object) to detect if a handler reassigned it.
|
|
*
|
|
* @param mixed $submittedValue The original submitted value (ID or array of IDs)
|
|
* @param mixed $dtoValue The DTO value after handler processing
|
|
*
|
|
* @return bool True if the values differ (handler modified the field)
|
|
*/
|
|
private function hasFieldValueChanged(mixed $submittedValue, mixed $dtoValue): bool
|
|
{
|
|
// Handle empty string as null (form submits empty string for unselected)
|
|
if ('' === $submittedValue) {
|
|
$submittedValue = null;
|
|
}
|
|
|
|
// Handle null cases
|
|
if (null === $dtoValue && null === $submittedValue) {
|
|
return false;
|
|
}
|
|
if (null === $dtoValue || null === $submittedValue) {
|
|
return true;
|
|
}
|
|
|
|
// Handle arrays (multiple selections like rentals, courses)
|
|
if (is_array($submittedValue) && is_array($dtoValue)) {
|
|
$submittedIds = array_map('strval', $submittedValue);
|
|
$dtoIds = array_map(fn ($item) => $this->extractId($item), $dtoValue);
|
|
sort($submittedIds);
|
|
sort($dtoIds);
|
|
|
|
return $submittedIds !== $dtoIds;
|
|
}
|
|
|
|
// Handle single service/insurance objects
|
|
$dtoId = $this->extractId($dtoValue);
|
|
|
|
// Compare as integers for reliable comparison (form may submit string, DTO has int)
|
|
return (int) $submittedValue !== (int) $dtoId;
|
|
}
|
|
|
|
/**
|
|
* Extracts the ID from a service, insurance, or pickup object.
|
|
*
|
|
* @param mixed $value The value to extract ID from
|
|
*
|
|
* @return string|null The extracted ID or null
|
|
*/
|
|
private function extractId(mixed $value): ?string
|
|
{
|
|
if ($value instanceof Service || $value instanceof Insurance || $value instanceof Pickup) {
|
|
return (string) $value->id;
|
|
}
|
|
|
|
if (is_scalar($value)) {
|
|
return (string) $value;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Returns handler names sorted by dependency order using cached results when possible.
|
|
*
|
|
* This method implements lazy loading with caching for performance. The dependency
|
|
* sort is only performed once and the result is cached until handlers are added
|
|
* or modified.
|
|
*
|
|
* @return string[] Array of handler field names in dependency execution order
|
|
*/
|
|
private function getSortedHandlers(): array
|
|
{
|
|
// Return cached result if available
|
|
if (null !== $this->sortedHandlers) {
|
|
return $this->sortedHandlers;
|
|
}
|
|
|
|
// Perform topological sort and cache the result
|
|
$this->sortedHandlers = $this->topologicalSort();
|
|
|
|
return $this->sortedHandlers;
|
|
}
|
|
|
|
/**
|
|
* Performs topological sort to determine safe handler execution order.
|
|
*
|
|
* This method uses Kahn's algorithm to sort handlers based on their declared
|
|
* dependencies. It ensures that no handler is executed before its dependencies
|
|
* have been processed, preventing data consistency issues.
|
|
*
|
|
* The algorithm works by:
|
|
* 1. Building a dependency graph of handlers
|
|
* 2. Finding handlers with no dependencies (in-degree = 0)
|
|
* 3. Iteratively removing handlers and updating dependencies
|
|
* 4. Detecting circular dependencies (deadlock prevention)
|
|
*
|
|
* @return string[] Array of handler field names in safe execution order
|
|
*
|
|
* @throws \InvalidArgumentException When a handler depends on a non-existent handler
|
|
* @throws \InvalidArgumentException When circular dependencies are detected
|
|
*/
|
|
private function topologicalSort(): array
|
|
{
|
|
$inDegree = []; // Count of dependencies for each handler
|
|
$graph = []; // Adjacency list of handler dependencies
|
|
$handlerNames = array_keys($this->handlers);
|
|
|
|
// Initialize all handlers with zero dependencies
|
|
foreach ($handlerNames as $handlerName) {
|
|
$inDegree[$handlerName] = 0;
|
|
$graph[$handlerName] = [];
|
|
}
|
|
|
|
// Build dependency graph by examining each handler's dependencies
|
|
foreach ($this->handlers as $handlerName => $handler) {
|
|
foreach ($handler->getDependencies() as $dependency) {
|
|
// Validate that the dependency exists
|
|
if (false === isset($this->handlers[$dependency])) {
|
|
throw new \InvalidArgumentException(sprintf('Handler "%s" depends on unknown handler "%s"', $handlerName, $dependency));
|
|
}
|
|
|
|
// Add edge from dependency to dependent handler
|
|
$graph[$dependency][] = $handlerName;
|
|
++$inDegree[$handlerName];
|
|
}
|
|
}
|
|
|
|
// Kahn's topological sort algorithm
|
|
$queue = []; // Handlers ready to be processed (no remaining dependencies)
|
|
$result = []; // Final sorted order
|
|
|
|
// Start with handlers that have no dependencies
|
|
foreach ($inDegree as $handlerName => $degree) {
|
|
if (0 === $degree) {
|
|
$queue[] = $handlerName;
|
|
}
|
|
}
|
|
|
|
// Process handlers in dependency order
|
|
while (false === empty($queue)) {
|
|
$current = array_shift($queue);
|
|
$result[] = $current;
|
|
|
|
// Remove this handler's dependencies from dependent handlers
|
|
foreach ($graph[$current] as $dependent) {
|
|
--$inDegree[$dependent];
|
|
// If dependent now has no remaining dependencies, add to queue
|
|
if (0 === $inDegree[$dependent]) {
|
|
$queue[] = $dependent;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Detect circular dependencies (if not all handlers were processed)
|
|
if (count($result) !== count($handlerNames)) {
|
|
throw new \InvalidArgumentException('Circular dependency detected in participant field handlers');
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* Synchronizes submitted data with the cleaned DTO state.
|
|
*
|
|
* This method updates the submitted form data to reflect any changes made by
|
|
* field handlers (such as clearing invalid service selections). This ensures
|
|
* that the form continues processing with cleaned data rather than the original
|
|
* submitted data that may contain invalid selections.
|
|
*
|
|
* In edit mode, this also adds the applicant's personal data to the submitted data
|
|
* for participant 0, ensuring those fields are bound to the DTO by handleRequest.
|
|
*
|
|
* The synchronization is generic and works with any field handlers by examining
|
|
* the current DTO state and updating the corresponding submitted data fields.
|
|
*
|
|
* @param array<string, mixed> $submittedData The original submitted form data
|
|
* @param BookingDto $bookingDto The DTO with cleaned data from field handlers
|
|
*
|
|
* @return array<string, mixed> Updated submitted data reflecting DTO state
|
|
*/
|
|
private function syncSubmittedDataWithDto(array $submittedData, BookingDto $bookingDto): array
|
|
{
|
|
// Ensure participants array exists in submitted data
|
|
if (false === isset($submittedData['participants']) || false === is_array($submittedData['participants'])) {
|
|
return $submittedData;
|
|
}
|
|
|
|
// Sync each participant's data with the cleaned DTO
|
|
foreach ($submittedData['participants'] as $index => $participantData) {
|
|
if (false === is_array($participantData)) {
|
|
continue;
|
|
}
|
|
|
|
$participant = $bookingDto->getParticipant((int) $index);
|
|
if (null === $participant) {
|
|
continue;
|
|
}
|
|
|
|
// Update participant data to match cleaned DTO state
|
|
$submittedData['participants'][$index] = $this->syncParticipantData($participantData, $participant, $index, $bookingDto);
|
|
}
|
|
|
|
return $submittedData;
|
|
}
|
|
|
|
/**
|
|
* Synchronizes individual participant submitted data with cleaned participant DTO.
|
|
*
|
|
* This method examines the participant DTO and updates the submitted data to match
|
|
* any changes made by field handlers. It automatically detects which fields have
|
|
* been processed by checking against registered handlers.
|
|
*
|
|
* In edit mode for participant 0 (applicant), this also adds the personal data fields
|
|
* that were patched from the applicant, ensuring they're bound to the DTO.
|
|
*
|
|
* @param array<string, mixed> $participantData The submitted participant data
|
|
* @param ParticipantDto $participant The cleaned participant DTO
|
|
* @param int $index The participant index
|
|
* @param BookingDto $bookingDto The booking DTO for mode detection
|
|
*
|
|
* @return array<string, mixed> Updated participant data with synchronized field values
|
|
*/
|
|
private function syncParticipantData(array $participantData, ParticipantDto $participant, int $index, BookingDto $bookingDto): array
|
|
{
|
|
// Sync fields for all registered handlers
|
|
foreach ($this->handlers as $fieldName => $handler) {
|
|
// 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};
|
|
$participantData[$fieldName] = $this->convertDtoValueToSubmittedFormat($dtoValue);
|
|
}
|
|
}
|
|
|
|
return $participantData;
|
|
}
|
|
|
|
/**
|
|
* Converts DTO field values to the format expected in submitted form data.
|
|
*
|
|
* This method handles the conversion from DTO field values to the format
|
|
* that Symfony forms expect in submitted data. It supports various data types
|
|
* including service objects, arrays, and primitive values.
|
|
*
|
|
* @param mixed $dtoValue The field value from the DTO
|
|
*
|
|
* @return mixed The value in submitted data format
|
|
*/
|
|
private function convertDtoValueToSubmittedFormat(mixed $dtoValue): mixed
|
|
{
|
|
// Handle null values
|
|
if (null === $dtoValue) {
|
|
return null;
|
|
}
|
|
|
|
// Handle arrays (service collections, etc.)
|
|
if (is_array($dtoValue)) {
|
|
$submittedFormat = [];
|
|
|
|
foreach ($dtoValue as $item) {
|
|
$submittedFormat[] = $this->convertSingleValueToSubmittedFormat($item);
|
|
}
|
|
|
|
return $submittedFormat;
|
|
}
|
|
|
|
// Handle single values
|
|
return $this->convertSingleValueToSubmittedFormat($dtoValue);
|
|
}
|
|
|
|
/**
|
|
* Checks whether a field handler is allowed to run based on edit-mode mutability flags.
|
|
*/
|
|
private function isHandlerMutableForBooking(string $handlerName, BookingDto $bookingDto): bool
|
|
{
|
|
if (BookingDto::MODE_CREATE === $bookingDto->getMode()) {
|
|
return true;
|
|
}
|
|
|
|
$mutabilityCategory = self::HANDLER_MUTABILITY_CATEGORY_MAP[$handlerName] ?? null;
|
|
|
|
return match ($mutabilityCategory) {
|
|
self::MUTABILITY_ADDITIONAL_SERVICES => $bookingDto->travel->additionalServicesMutable,
|
|
self::MUTABILITY_TRANSPORTATION => $bookingDto->travel->transportationServicesMutable,
|
|
self::MUTABILITY_PICKUPS => $bookingDto->travel->pickupsMutable,
|
|
default => true,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Converts a single DTO value to submitted form format.
|
|
*
|
|
* @param mixed $value The value to convert
|
|
*
|
|
* @return mixed The converted value
|
|
*/
|
|
private function convertSingleValueToSubmittedFormat(mixed $value): mixed
|
|
{
|
|
// Handle Service objects -> convert to ID (as string for form compatibility)
|
|
if ($value instanceof Service) {
|
|
return (string) $value->id;
|
|
}
|
|
|
|
// Handle Pickup objects -> convert to ID (as string for form compatibility)
|
|
if ($value instanceof Pickup) {
|
|
return (string) $value->id;
|
|
}
|
|
|
|
// Handle Insurance objects -> convert to ID (as string for form compatibility)
|
|
if ($value instanceof Insurance) {
|
|
return (string) $value->id;
|
|
}
|
|
|
|
// Handle Address objects -> convert to array of properties
|
|
if ($value instanceof Address) {
|
|
return [
|
|
'street' => $value->street,
|
|
'postCode' => $value->postCode,
|
|
'city' => $value->city,
|
|
'district' => $value->district,
|
|
'country' => $value->country,
|
|
];
|
|
}
|
|
|
|
// Handle DateTimeInterface -> convert to array format for BirthdayType widget
|
|
if ($value instanceof \DateTimeInterface) {
|
|
return [
|
|
'year' => $value->format('Y'),
|
|
'month' => $value->format('n'),
|
|
'day' => $value->format('j'),
|
|
];
|
|
}
|
|
|
|
// Handle numeric values
|
|
if (is_numeric($value)) {
|
|
return $value;
|
|
}
|
|
|
|
// Handle strings and other primitive types
|
|
return $value;
|
|
}
|
|
}
|