Files
ephub/src/Controller/BoardController.php
T

93 lines
3.3 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Controller;
use App\Booking\BoardPresenter;
use App\Booking\DateParam;
use App\Booking\TimeRange;
use App\Entity\ResourceType;
use App\Htmx\HxTrait;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
/**
* One booking board per resource kind: a month calendar next to that kind's floor plan.
*
* The kind is a route parameter rather than a separate controller per kind, so a new
* ResourceType case gets a working board without touching this file.
*/
final class BoardController extends AbstractController
{
use CurrentUserTrait;
use HxTrait;
public function __construct(private readonly BoardPresenter $board)
{
}
#[Route('/book/{type}', name: 'app_board', requirements: ['type' => '[a-z]+'], methods: ['GET'])]
public function index(Request $request, string $type): Response
{
$resourceType = $this->resourceType($type);
$date = DateParam::parseOrToday($request->query->getString('date'));
return $this->render('board/index.html.twig', $this->board->context(
$resourceType,
$date,
self::timeOf($request),
$this->currentUser(),
));
}
/**
* Picking a day -- or narrowing the span -- swaps the calendar and the floor plan in one
* response.
*
* This is the whole reason htmxOobResponse exists: two regions, one round trip, no
* client-side event choreography. Requests without the htmx header get a plain redirect
* rather than a bare pair of fragments, so the URL stays shareable.
*/
#[Route('/book/{type}/day/{date}', name: 'app_board_day', requirements: ['type' => '[a-z]+', 'date' => DateParam::PATTERN], methods: ['GET'])]
public function day(Request $request, string $type, string $date): Response
{
$resourceType = $this->resourceType($type);
$selected = DateParam::parseOrToday($date);
$time = self::timeOf($request);
if (!$request->headers->has('HX-Request')) {
return $this->redirectToRoute('app_board', BoardPresenter::urlParameters($resourceType, $selected, $time));
}
return $this->htmxOobResponse(
'board/index.html.twig',
['calendar', 'floorplan'],
$this->board->context($resourceType, $selected, $time, $this->currentUser()),
$this->generateUrl('app_board', BoardPresenter::urlParameters($resourceType, $selected, $time)),
);
}
/**
* A malformed or missing span falls back to the whole day rather than erroring: these are
* hand-editable query parameters, not route requirements.
*/
public static function timeOf(Request $request): TimeRange
{
return TimeRange::parse($request->query->getString('from'), $request->query->getString('to'))
?? TimeRange::wholeDay();
}
/**
* Route requirements cannot enumerate the enum, so {type} is matched loosely and resolved
* here -- an unknown kind is a 404 rather than a 500.
*/
private function resourceType(string $value): ResourceType
{
return ResourceType::tryFrom($value)
?? throw $this->createNotFoundException(\sprintf('Unknown bookable kind "%s".', $value));
}
}