'[a-z]+'], methods: ['GET'])] public function index(Request $request, string $type): Response { $resourceType = $this->resourceType($type); $date = DateParam::parseOrToday($request->query->getString('tag')); 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('/buchen/{type}/tag/{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('von'), $request->query->getString('bis')) ?? TimeRange::wholeDay(); } /** * Route requirements cannot call a method, so the slug is matched loosely and resolved here. */ private function resourceType(string $slug): ResourceType { try { return ResourceType::fromSlug($slug); } catch (\ValueError $e) { throw $this->createNotFoundException($e->getMessage(), $e); } } }