wip: transportation services
This commit is contained in:
1 parent
a019c7dae0
commit
81b8237570
18 files changed
+1113
-59
No files matched your search
@@ -87,6 +87,7 @@ class RoomSelectionCondition implements FieldConditionInterface
|
||||
public function getDescription(): string
|
||||
{
|
||||
$codes = implode(', ', $this->requiredRoomCodes);
|
||||
|
||||
return "Field visible when room with code(s) [{$codes}] is selected";
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\BusProNet\Model\Service;
|
||||
use App\Form\Model\BookingDtoInterface;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
* Condition that evaluates service sub-types.
|
||||
*
|
||||
* This condition enables field state logic based on the sub-type property
|
||||
* of Service objects. It can be used with any service field (transportation,
|
||||
* additional services, etc.) to show/hide or enable/disable dependent fields
|
||||
* based on the selected service type.
|
||||
*
|
||||
* Common Use Cases:
|
||||
* - Show/hide pickup fields when transportation sub-type is BUS
|
||||
* - Show/hide parking fields when transportation sub-type is PKW
|
||||
* - Enable/disable fields based on service category selection
|
||||
* - Conditional logic for different service types
|
||||
*/
|
||||
class ServiceSubTypeCondition implements FieldConditionInterface
|
||||
{
|
||||
private const OPERATOR_EQUALS = 'equals';
|
||||
private const OPERATOR_NOT_EQUALS = 'not_equals';
|
||||
private const OPERATOR_IN = 'in';
|
||||
private const OPERATOR_NOT_IN = 'not_in';
|
||||
|
||||
/**
|
||||
* Creates a new service sub-type condition.
|
||||
*
|
||||
* @param string $serviceFieldName The name of the service field to check
|
||||
* @param string $operator The comparison operator to use
|
||||
* @param string|string[] $expectedSubType The expected service sub-type(s)
|
||||
*/
|
||||
public function __construct(
|
||||
private readonly string $serviceFieldName,
|
||||
private readonly string $operator,
|
||||
private readonly string|array $expectedSubType,
|
||||
) {
|
||||
$this->validateOperator($operator);
|
||||
$this->validateExpectedSubType($operator, $expectedSubType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluates the service sub-type condition against current form data.
|
||||
*
|
||||
* Retrieves the service from the specified field and checks its subType
|
||||
* property against the expected value(s). Handles both form data and
|
||||
* participant DTO data sources.
|
||||
*
|
||||
* @param BookingDtoInterface $bookingDto The current booking data (create or edit)
|
||||
* @param int $participantIndex The index of the participant being evaluated
|
||||
* @param array<string, mixed> $formData Current form data for condition evaluation
|
||||
*
|
||||
* @return bool True if the service sub-type meets the condition criteria, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDtoInterface $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
$service = $this->getService($formData, $participantIndex, $bookingDto);
|
||||
|
||||
if (null === $service) {
|
||||
// If no service is selected, condition fails
|
||||
return false;
|
||||
}
|
||||
|
||||
$actualSubType = $service->subType ?? '';
|
||||
|
||||
return match ($this->operator) {
|
||||
self::OPERATOR_EQUALS => $actualSubType === $this->expectedSubType,
|
||||
self::OPERATOR_NOT_EQUALS => $actualSubType !== $this->expectedSubType,
|
||||
self::OPERATOR_IN => $this->isSubTypeIn($actualSubType, $this->expectedSubType),
|
||||
self::OPERATOR_NOT_IN => !$this->isSubTypeIn($actualSubType, $this->expectedSubType),
|
||||
default => false,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field names that this condition depends on.
|
||||
*
|
||||
* This condition depends on the service field being evaluated, so any changes
|
||||
* to that field should trigger re-evaluation of dependent field states.
|
||||
*
|
||||
* @return string[] Array containing the service field name being evaluated
|
||||
*/
|
||||
public function getDependentFields(): array
|
||||
{
|
||||
return [$this->serviceFieldName];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-readable description of the service sub-type condition.
|
||||
*
|
||||
* Generates a descriptive string explaining what service field is being
|
||||
* checked and what the expected sub-type criteria are.
|
||||
*
|
||||
* @return string Description of the service sub-type condition
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
$subTypeDescription = is_array($this->expectedSubType)
|
||||
? '['.implode(', ', $this->expectedSubType).']'
|
||||
: (string) $this->expectedSubType;
|
||||
|
||||
return match ($this->operator) {
|
||||
self::OPERATOR_EQUALS => sprintf('Service "%s" sub-type equals %s', $this->serviceFieldName, $subTypeDescription),
|
||||
self::OPERATOR_NOT_EQUALS => sprintf('Service "%s" sub-type does not equal %s', $this->serviceFieldName, $subTypeDescription),
|
||||
self::OPERATOR_IN => sprintf('Service "%s" sub-type is in %s', $this->serviceFieldName, $subTypeDescription),
|
||||
self::OPERATOR_NOT_IN => sprintf('Service "%s" sub-type is not in %s', $this->serviceFieldName, $subTypeDescription),
|
||||
default => sprintf('Service "%s" sub-type %s %s', $this->serviceFieldName, $this->operator, $subTypeDescription),
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition for service sub-type equality.
|
||||
*
|
||||
* @param string $serviceFieldName The service field to check
|
||||
* @param string $expectedSubType The expected service sub-type
|
||||
*/
|
||||
public static function equals(string $serviceFieldName, string $expectedSubType): self
|
||||
{
|
||||
return new self($serviceFieldName, self::OPERATOR_EQUALS, $expectedSubType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition for service sub-type non-equality.
|
||||
*
|
||||
* @param string $serviceFieldName The service field to check
|
||||
* @param string $expectedSubType The sub-type that should not match
|
||||
*/
|
||||
public static function notEquals(string $serviceFieldName, string $expectedSubType): self
|
||||
{
|
||||
return new self($serviceFieldName, self::OPERATOR_NOT_EQUALS, $expectedSubType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition for service sub-type inclusion.
|
||||
*
|
||||
* @param string $serviceFieldName The service field to check
|
||||
* @param string[] $allowedSubTypes The allowed sub-types
|
||||
*/
|
||||
public static function in(string $serviceFieldName, array $allowedSubTypes): self
|
||||
{
|
||||
return new self($serviceFieldName, self::OPERATOR_IN, $allowedSubTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a condition for service sub-type exclusion.
|
||||
*
|
||||
* @param string $serviceFieldName The service field to check
|
||||
* @param string[] $excludedSubTypes The excluded sub-types
|
||||
*/
|
||||
public static function notIn(string $serviceFieldName, array $excludedSubTypes): self
|
||||
{
|
||||
return new self($serviceFieldName, self::OPERATOR_NOT_IN, $excludedSubTypes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves service from form data or participant data.
|
||||
*/
|
||||
private function getService(array $formData, int $participantIndex, BookingDtoInterface $bookingDto): ?Service
|
||||
{
|
||||
// First check participant-specific form data
|
||||
if (isset($formData['participants'][$participantIndex][$this->serviceFieldName])) {
|
||||
$serviceData = $formData['participants'][$participantIndex][$this->serviceFieldName];
|
||||
|
||||
// Form data might contain Service objects or service IDs
|
||||
if ($serviceData instanceof Service) {
|
||||
return $serviceData;
|
||||
}
|
||||
}
|
||||
|
||||
// Then check participant DTO data
|
||||
$participant = $bookingDto->getParticipant($participantIndex);
|
||||
if (null !== $participant && property_exists($participant, $this->serviceFieldName)) {
|
||||
return $participant->{$this->serviceFieldName};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if sub-type is in the expected array.
|
||||
*/
|
||||
private function isSubTypeIn(string $actualSubType, string|array $expectedSubTypes): bool
|
||||
{
|
||||
if (is_string($expectedSubTypes)) {
|
||||
return $actualSubType === $expectedSubTypes;
|
||||
}
|
||||
|
||||
return in_array($actualSubType, $expectedSubTypes, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that the operator is supported.
|
||||
*/
|
||||
private function validateOperator(string $operator): void
|
||||
{
|
||||
$validOperators = [
|
||||
self::OPERATOR_EQUALS,
|
||||
self::OPERATOR_NOT_EQUALS,
|
||||
self::OPERATOR_IN,
|
||||
self::OPERATOR_NOT_IN,
|
||||
];
|
||||
|
||||
if (!in_array($operator, $validOperators, true)) {
|
||||
throw new \InvalidArgumentException(sprintf('Invalid operator "%s". Valid operators: %s', $operator, implode(', ', $validOperators)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates expected sub-type based on operator requirements.
|
||||
*/
|
||||
private function validateExpectedSubType(string $operator, string|array $expectedSubType): void
|
||||
{
|
||||
if (in_array($operator, [self::OPERATOR_IN, self::OPERATOR_NOT_IN], true) && !is_array($expectedSubType)) {
|
||||
throw new \InvalidArgumentException(sprintf('Operator "%s" requires expectedSubType to be an array', $operator));
|
||||
}
|
||||
|
||||
if (in_array($operator, [self::OPERATOR_EQUALS, self::OPERATOR_NOT_EQUALS], true) && !is_string($expectedSubType)) {
|
||||
throw new \InvalidArgumentException(sprintf('Operator "%s" requires expectedSubType to be a string', $operator));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user