feat: readonly room selection for single room types
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Form\Service\Condition;
|
||||
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Service\Contract\FieldConditionInterface;
|
||||
|
||||
/**
|
||||
* Condition that evaluates whether only a single room type is selected in the booking.
|
||||
*
|
||||
* This condition checks if exactly one room type has been selected with a positive quantity
|
||||
* in the booking's room selections. When only one room type is available, the room assignment
|
||||
* dropdown becomes unnecessary and can be made readonly to improve UX.
|
||||
*/
|
||||
class SingleRoomTypeCondition implements FieldConditionInterface
|
||||
{
|
||||
/**
|
||||
* Evaluates whether exactly one room type is selected in the booking.
|
||||
*
|
||||
* Counts the number of room types that have been selected with a quantity greater than zero.
|
||||
* Returns true when exactly one room type is selected, false otherwise.
|
||||
*
|
||||
* @param BookingDto $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 exactly one room type is selected, false otherwise
|
||||
*/
|
||||
public function evaluate(BookingDto $bookingDto, int $participantIndex, array $formData): bool
|
||||
{
|
||||
$selectedRoomCount = 0;
|
||||
|
||||
foreach ($bookingDto->roomSelections as $roomSelection) {
|
||||
if (null !== $roomSelection->quantity && $roomSelection->quantity > 0) {
|
||||
++$selectedRoomCount;
|
||||
}
|
||||
}
|
||||
|
||||
return 1 === $selectedRoomCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns field names that trigger re-evaluation of this condition.
|
||||
*
|
||||
* This condition depends on booking-level room selection data, not participant fields.
|
||||
* Room selections are determined in step 1 and remain static during participant editing,
|
||||
* so no participant-level field dependencies are needed.
|
||||
*
|
||||
* @return string[] Empty array (no participant-level dependencies)
|
||||
*/
|
||||
public function getDependentFields(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a human-readable description of this condition.
|
||||
*
|
||||
* @return string A brief description of the condition logic
|
||||
*/
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Field readonly when exactly one room type is selected';
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ use App\Form\Service\Condition\FieldValueCondition;
|
||||
use App\Form\Service\Condition\RentalSelectionCondition;
|
||||
use App\Form\Service\Condition\RoomSelectionCondition;
|
||||
use App\Form\Service\Condition\ServiceSubTypeCondition;
|
||||
use App\Form\Service\Condition\SingleRoomTypeCondition;
|
||||
use App\Form\Service\Condition\SkiPassSelectionCondition;
|
||||
use App\Service\ParticipantEligibilityService;
|
||||
|
||||
@@ -204,6 +205,11 @@ class CreateFieldStateProvider extends AbstractFieldStateProvider
|
||||
'required' => new ApplicantCondition(),
|
||||
];
|
||||
|
||||
// Make assignedRoomId readonly when only one room type is selected
|
||||
$this->fieldStateConditions['assignedRoomId'] = [
|
||||
'readonly' => new SingleRoomTypeCondition(),
|
||||
];
|
||||
|
||||
// Example field state conditions would be registered here
|
||||
// For demonstration purposes, here are some example patterns:
|
||||
|
||||
|
||||
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Form\Service\Condition;
|
||||
|
||||
use App\BusProNet\Model\Travel;
|
||||
use App\Form\Model\BookingDto;
|
||||
use App\Form\Model\RoomSelectionDto;
|
||||
use App\Form\Service\Condition\SingleRoomTypeCondition;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SingleRoomTypeConditionTest extends TestCase
|
||||
{
|
||||
private SingleRoomTypeCondition $condition;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
$this->condition = new SingleRoomTypeCondition();
|
||||
}
|
||||
|
||||
public function testReturnsFalseWhenNoRoomsSelected(): void
|
||||
{
|
||||
$bookingDto = $this->createBookingDto([]);
|
||||
|
||||
$result = $this->condition->evaluate($bookingDto, 0, []);
|
||||
|
||||
$this->assertFalse($result, 'Condition should return false when no rooms are selected');
|
||||
}
|
||||
|
||||
public function testReturnsTrueWhenExactlyOneRoomSelected(): void
|
||||
{
|
||||
$bookingDto = $this->createBookingDto([
|
||||
['roomId' => 1, 'quantity' => 2],
|
||||
]);
|
||||
|
||||
$result = $this->condition->evaluate($bookingDto, 0, []);
|
||||
|
||||
$this->assertTrue($result, 'Condition should return true when exactly one room type is selected');
|
||||
}
|
||||
|
||||
public function testReturnsFalseWhenTwoRoomsSelected(): void
|
||||
{
|
||||
$bookingDto = $this->createBookingDto([
|
||||
['roomId' => 1, 'quantity' => 2],
|
||||
['roomId' => 2, 'quantity' => 1],
|
||||
]);
|
||||
|
||||
$result = $this->condition->evaluate($bookingDto, 0, []);
|
||||
|
||||
$this->assertFalse($result, 'Condition should return false when two room types are selected');
|
||||
}
|
||||
|
||||
public function testReturnsFalseWhenMultipleRoomsSelected(): void
|
||||
{
|
||||
$bookingDto = $this->createBookingDto([
|
||||
['roomId' => 1, 'quantity' => 2],
|
||||
['roomId' => 2, 'quantity' => 1],
|
||||
['roomId' => 3, 'quantity' => 3],
|
||||
]);
|
||||
|
||||
$result = $this->condition->evaluate($bookingDto, 0, []);
|
||||
|
||||
$this->assertFalse($result, 'Condition should return false when multiple room types are selected');
|
||||
}
|
||||
|
||||
public function testIgnoresRoomsWithZeroQuantity(): void
|
||||
{
|
||||
$bookingDto = $this->createBookingDto([
|
||||
['roomId' => 1, 'quantity' => 2],
|
||||
['roomId' => 2, 'quantity' => 0],
|
||||
['roomId' => 3, 'quantity' => 0],
|
||||
]);
|
||||
|
||||
$result = $this->condition->evaluate($bookingDto, 0, []);
|
||||
|
||||
$this->assertTrue($result, 'Condition should ignore rooms with zero quantity');
|
||||
}
|
||||
|
||||
public function testIgnoresRoomsWithNullQuantity(): void
|
||||
{
|
||||
$bookingDto = $this->createBookingDto([
|
||||
['roomId' => 1, 'quantity' => 1],
|
||||
['roomId' => 2, 'quantity' => null],
|
||||
]);
|
||||
|
||||
$result = $this->condition->evaluate($bookingDto, 0, []);
|
||||
|
||||
$this->assertTrue($result, 'Condition should ignore rooms with null quantity');
|
||||
}
|
||||
|
||||
public function testReturnsFalseWhenAllRoomsHaveZeroQuantity(): void
|
||||
{
|
||||
$bookingDto = $this->createBookingDto([
|
||||
['roomId' => 1, 'quantity' => 0],
|
||||
['roomId' => 2, 'quantity' => 0],
|
||||
]);
|
||||
|
||||
$result = $this->condition->evaluate($bookingDto, 0, []);
|
||||
|
||||
$this->assertFalse($result, 'Condition should return false when all rooms have zero quantity');
|
||||
}
|
||||
|
||||
public function testGetDependentFieldsReturnsEmptyArray(): void
|
||||
{
|
||||
$result = $this->condition->getDependentFields();
|
||||
|
||||
$this->assertIsArray($result);
|
||||
$this->assertEmpty($result, 'Single room type condition has no participant-level field dependencies');
|
||||
}
|
||||
|
||||
public function testGetDescriptionReturnsString(): void
|
||||
{
|
||||
$result = $this->condition->getDescription();
|
||||
|
||||
$this->assertIsString($result);
|
||||
$this->assertStringContainsString('readonly', $result);
|
||||
$this->assertStringContainsString('one room type', $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a BookingDto with specified room selections.
|
||||
*
|
||||
* @param array<int, array{roomId: int, quantity: int|null}> $rooms
|
||||
*/
|
||||
private function createBookingDto(array $rooms): BookingDto
|
||||
{
|
||||
$travel = new Travel();
|
||||
$bookingDto = new BookingDto($travel, 1);
|
||||
|
||||
foreach ($rooms as $roomData) {
|
||||
$roomSelection = new RoomSelectionDto();
|
||||
$roomSelection->roomId = $roomData['roomId'];
|
||||
$roomSelection->quantity = $roomData['quantity'];
|
||||
$bookingDto->roomSelections[] = $roomSelection;
|
||||
}
|
||||
|
||||
return $bookingDto;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user