<?php
declare(strict_types=1);
namespace App\Controller\Website;
use App\Repository\Cruise\CruiseRepository;
use App\Repository\Price\PriceRepository;
use Croisiland\CommonBundle\Constants\Cruise\CruiseConstants;
use Croisiland\CommonBundle\Entity\Cruise\Cruise;
use DateMalformedStringException;
use Doctrine\ORM\NonUniqueResultException;
use Doctrine\ORM\NoResultException;
use Sulu\Component\Content\Compat\StructureInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class DetailController extends BaseWebsiteController
{
const string BASE_PATH = '/croisieres/details';
public function __construct(
private readonly CruiseRepository $cruiseRepository,
private readonly PriceRepository $priceRepository,
) {
}
/**
* @throws DateMalformedStringException
* @throws NonUniqueResultException
* @throws NoResultException
*/
public function indexAction(StructureInterface $structure, $preview = false, $partial = false): Response
{
$cruise = $this->resolveCruise();
// TODO check if partner has access to the cruise with :
// $this->>partnerHelper->currentPartnerHasAccessTo($cruise)
// redirect to a 403 page
$upComingCruiseIds = $this->cruiseRepository->getUpComingCruiseIds($cruise->getItinerary(), CruiseConstants::DATETIME_INTERVAL_NEXT_DEPARTURES);
$bestUpComingPrice = $this->priceRepository->findBestPriceForCruiseIds($upComingCruiseIds);
$attributes = array_merge($this->getSharedLayoutFormViews(), [
'cruise' => $cruise,
'bestUpComingPrice' => $bestUpComingPrice,
]);
return $this->renderStructure(
$structure,
$attributes,
$preview,
$partial
);
}
/**
* Fragment HTML du bloc tarifs (vue tableau ou vue détaillée).
*
* @throws DateMalformedStringException
* @throws NonUniqueResultException
* @throws NoResultException
*/
#[Route('/croisieres/details/fragments/pricing', name: 'website_detail_pricing_fragment', methods: ['GET'])]
public function pricingFragmentAction(Request $request): Response
{
$cruiseId = $request->query->getInt('croisiere', 0);
$view = $request->query->get('view', 'table');
if ($cruiseId <= 0 || !in_array($view, ['table', 'detailed'], true)) {
return new Response('Invalid parameters', Response::HTTP_BAD_REQUEST);
}
$cruise = $this->cruiseRepository->find($cruiseId);
if (!$cruise instanceof Cruise) {
return new Response('Cruise not found', Response::HTTP_NOT_FOUND);
}
$upComingCruiseIds = $this->cruiseRepository->getUpComingCruiseIds(
$cruise->getItinerary(),
CruiseConstants::DATETIME_INTERVAL_NEXT_DEPARTURES
);
$bestUpComingPrice = $this->priceRepository->findBestPriceForCruiseIds($upComingCruiseIds);
return $this->render('pages/_includes/details/_other-departures-pricing-fragment.html.twig', [
'view' => $view,
'currentCruise' => $cruise,
'bestUpComingPrice' => $bestUpComingPrice,
]);
}
/**
* Charge la croisière courante depuis la querystring `?croisiere=<id>`
* (cohérent avec le lien généré par `_snippets/components/cards/list_card.html.twig`).
*
* Retourne `null` si :
* - la requête n'est pas disponible (rendu hors HTTP, preview, etc.),
* - aucun id n'est fourni,
* - aucune croisière ne correspond à l'id.
*
* Le template doit donc se montrer défensif sur `cruise` (ce que fait `list_card`).
*/
private function resolveCruise(): ?Cruise
{
$request = $this->container->get('request_stack')->getCurrentRequest();
if ($request === null) {
return null;
}
$id = $request->query->getInt('croisiere', 0);
if ($id <= 0) {
return null;
}
return $this->cruiseRepository->find($id);
}
}