getCalendarCode()) { $cmsData = $this->cmsDataProvider->getHotelDetails($accommodation->getEffectiveCmsCode()); } $form = $this->createForm(AccommodationType::class, $accommodation); $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { $this->entityManager->persist($accommodation); $this->entityManager->flush(); $this->addFlash('success', 'Das Gruppenhaus wurde aktualisiert'); $this->logger->info('Updated accommodation', [ 'name' => $accommodation->getName(), 'code' => $accommodation->getCalendarCode(), ]); return $this->redirectToRoute('app_admin_accommodation_edit', ['id' => $accommodation->getId()]); } $boardServices = $accommodation->getBoardServices()->toArray(); usort($boardServices, $this->compareServices(...)); // Additional services are ordered manually, see #[ORM\OrderBy] on the association. $additionalServices = $accommodation->getAdditionalServices()->toArray(); return $this->render('admin/accommodation/edit.html.twig', [ 'accommodation' => $accommodation, 'form' => $form, 'cmsData' => $cmsData, 'boardServices' => $boardServices, 'additionalServices' => $additionalServices, ]); } /** * Receives the additional service ids in their new order and renumbers the positions. * * Ids are only accepted when they belong to the given accommodation; services missing from the * payload (e.g. created in another tab in the meantime) are appended in their previous order. */ #[Route('/admin/accommodation/{id}/additional-services/order', name: 'app_admin_accommodation_additionalservices_order', methods: ['POST'])] public function orderAdditionalServices(Accommodation $accommodation, Request $request): Response { try { $payload = json_decode($request->getContent(), true, 512, \JSON_THROW_ON_ERROR); } catch (\JsonException) { return new JsonResponse(['error' => 'Malformed payload'], Response::HTTP_BAD_REQUEST); } if (!\is_array($payload) || !\is_array($payload['ids'] ?? null)) { return new JsonResponse(['error' => 'Malformed payload'], Response::HTTP_BAD_REQUEST); } /** @var array $remaining */ $remaining = []; foreach ($accommodation->getAdditionalServices() as $additionalService) { $remaining[$additionalService->getId()] = $additionalService; } $position = 0; foreach ($payload['ids'] as $id) { $id = \is_numeric($id) ? (int) $id : null; if (null === $id || !isset($remaining[$id])) { continue; } $remaining[$id]->setPosition(++$position); unset($remaining[$id]); } // Whatever was not part of the payload keeps its relative order behind the sorted ones. foreach ($remaining as $additionalService) { $additionalService->setPosition(++$position); } $this->entityManager->flush(); return new JsonResponse(['status' => 'ok']); } /** * DateFrom ascending, then free (price 0) before paid, then alphabetically by label. */ private function compareServices(BoardService $a, BoardService $b): int { $aFree = 0 === $a->getPrice() ? 0 : 1; $bFree = 0 === $b->getPrice() ? 0 : 1; return $a->getDateFrom() <=> $b->getDateFrom() ?: $aFree <=> $bFree ?: strnatcasecmp($a->getLabel(), $b->getLabel()); } }