src/Controller/Front/Product/HotelController.php line 508

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Front\Product;
  3. use App\Config\FaqRouteEnum;
  4. use App\Config\ModuleEnum;
  5. use App\Entity\CustomerMoral;
  6. use App\Repository\CityRepository;
  7. use App\Repository\FrontThemeRepository;
  8. use App\Repository\HotelXmlPriceRepository;
  9. use App\Repository\HotelXmlRepository;
  10. use App\Repository\OrderLineRepository;
  11. use App\Repository\OrderRepository;
  12. use App\Service\Api3TAuthenticationService;
  13. use App\Service\CartService;
  14. use App\Service\CurrencyService;
  15. use App\Service\FrontRecentSearchService;
  16. use App\Service\FrontService;
  17. use App\Service\Helpers;
  18. use App\Service\HotelApiService;
  19. use App\Service\ParameterService;
  20. use DateTime;
  21. use Exception;
  22. use Gedmo\Translatable\TranslatableListener;
  23. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  24. use Symfony\Component\HttpClient\HttpClient;
  25. use Symfony\Component\HttpFoundation\JsonResponse;
  26. use Symfony\Component\HttpFoundation\RedirectResponse;
  27. use Symfony\Component\HttpFoundation\Request;
  28. use Symfony\Component\HttpFoundation\Response;
  29. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  30. use Symfony\Component\Routing\Annotation\Route;
  31. #[Route('/hotel')]
  32. class HotelController extends AbstractController
  33. {
  34.     public function __construct(
  35.         private readonly Api3TAuthenticationService $apiAuthenticationService,
  36.         private readonly HotelApiService            $hotelApiService,
  37.         private readonly CurrencyService            $currencyService,
  38.         private readonly FrontService               $frontService,
  39.         private readonly ParameterService           $parameterService,
  40.         private readonly Helpers                    $helpers,
  41.     ){
  42.     }
  43.     #[Route('/hotelcity/{name}'name'app_front_hotel_city')]
  44.     public function hotelByCity(
  45.         string                  $name,
  46.         CityRepository          $cityRepository,
  47.         HotelXmlPriceRepository $hotelXmlPriceRepository,
  48.         FrontService            $frontService
  49.     ): Response {
  50.         $hotels = [];
  51.         $city $cityRepository->findOneBy(['name' => $name]);
  52.         $description "";
  53.         $country "";
  54.         if ($city) {
  55.             $description $city->getDescription();
  56.             $country $city->getCountry()->getName();
  57.         }
  58.         $hotelXmlPrices $hotelXmlPriceRepository->getHotelXmlPriceByCriterias(['cities' => [$name]]);
  59.         foreach ($hotelXmlPrices as $HotelXmlPrice) {
  60.             $hotels[] = $frontService->getItemHotel($HotelXmlPrice);
  61.         }
  62.         return $this->render('front/hotel/hotel_by_city.html.twig', [
  63.             'hotels' => $hotels,
  64.             'city' => $city,
  65.             'name' => $name,
  66.             'country' => $country,
  67.             'society' => $this->parameterService->getSocietyParameters(),
  68.             'social_networks' => $this->frontService->getSocialNetworks(),
  69.             'agencies' => $this->frontService->getAgencies(),
  70.             'currencies' => $this->frontService->getCurrencies(),
  71.             'currencySwitcher' => true,
  72.             'tracking_tools' => $this->frontService->getTrackingTools(),
  73.             'faqs' => $this->frontService->getFaqs(FaqRouteEnum::APP_FRONT_HOTEL_CITYstrtolower($name)),
  74.         ]);
  75.     }
  76.     #[Route('/hoteltheme/{name}'name'app_front_hotel_theme')]
  77.     public function hotelByTheme(
  78.         string                  $name,
  79.         FrontThemeRepository    $frontThemeRepository,
  80.         HotelXmlPriceRepository $hotelXmlPriceRepository,
  81.         FrontService            $frontService
  82.     ): Response {
  83.         $hotels = [];
  84.         $theme $frontThemeRepository->findOneBy(['name' => $name]);
  85.         $description "";
  86.         if ($theme) {
  87.             $description $theme->getDescription();
  88.         }
  89.         $hotelXmlPrices $hotelXmlPriceRepository->getHotelXmlPriceByCriterias(['themes' => $name]);
  90.         foreach ($hotelXmlPrices as $HotelXmlPrice) {
  91.             $hotels[] = $frontService->getItemHotel($HotelXmlPrice);
  92.         }
  93.         return $this->render('front/hotel/hotel_by_theme.html.twig', [
  94.             'hotels' => $hotels,
  95.             'description' => $description,
  96.             'name' => $name,
  97.             'society' => $this->parameterService->getSocietyParameters(),
  98.             'social_networks' => $this->frontService->getSocialNetworks(),
  99.             'agencies' => $this->frontService->getAgencies(),
  100.             'currencies' => $this->frontService->getCurrencies(),
  101.             'tracking_tools' => $this->frontService->getTrackingTools()
  102.         ]);
  103.     }
  104.     #[Route('/search'name'hotel_search'methods: ['GET''POST'])]
  105.     public function hotelSearch(Request $requestFrontRecentSearchService $frontRecentSearchService): Response
  106.     {
  107.         $data $request->get("data"); // data : JSON request
  108.         $requestData json_decode($datatrue);
  109.         $date = new DateTime();
  110.         // Output the date in default format
  111.         $currentDate $date->format('Y-m-d'); // Example: 2025-01-02 15:30:45
  112.         if ($requestData['checkIn'] < $currentDate || $requestData['checkOut'] < $currentDate) {
  113.             return $this->redirectToRoute('app_main');
  114.         }
  115.         $customer $this->apiAuthenticationService->getCustomer($this->getUser());
  116.         $hotel_search_history $frontRecentSearchService->updateHotelSearchHistory($requestData$data);
  117.         $frontMenus $this->frontService->getFrontMenu();
  118.         return $this->render('front/hotel/search.html.twig', [
  119.             'requestData' => $requestData,
  120.             'customer' => $customer,
  121.             'society' => $this->parameterService->getSocietyParameters(),
  122.             'social_networks' => $this->frontService->getSocialNetworks(),
  123.             'agencies' => $this->frontService->getAgencies(),
  124.             'currencies' => $this->frontService->getCurrencies(),
  125.             'hotel_search_history' => $hotel_search_history,
  126.             'sections' => $this->frontService->getSections(),
  127.             'currencySwitcher' => true,
  128.             'tracking_tools' => $this->frontService->getTrackingTools()
  129.         ]);
  130.     }
  131.     #[Route('/formsearch/{city_label}/{hotel_label}'name'hotel_form_search'methods: ['GET''POST'])]
  132.     public function hotelFormSearch(Request $requeststring $city_labelstring $hotel_label): Response
  133.     {
  134.         $requestData json_decode($request->get("data"), true);
  135.         return $this->render('front/hotel/form_search.html.twig', [
  136.             'requestData' => $requestData,
  137.             'hotel_label' => $hotel_label,
  138.             'city_label' => $city_label
  139.             /* 'society' => $this->parameterService->getSocietyParameters(),
  140.              'social_networks' => $this->frontService->getSocialNetworks(),
  141.              'agencies' => $this->frontService->getAgencies(),
  142.              'currencies' => $this->frontService->getCurrencies(),*/
  143.         ]);
  144.     }
  145.     #[Route('/search_json'name'hotel_search_json'methods: ['GET''POST'])]
  146.     public function hotelSearchJson(Request $requestHotelApiService $hotelApiService): JsonResponse
  147.     {
  148.         // ----- PREPARE the HttpRequestCall ----- //
  149.         // Get auth_data : user, timestamp, signature
  150.         $user $this->getUser();
  151.         $auth_data $this->apiAuthenticationService->getAuthenticationArray($user);
  152.         $customer $this->apiAuthenticationService->getCustomer($user);
  153.         $showResellingRates false;
  154.         if ($customer instanceof CustomerMoral) {
  155.             if ($customer->getResellerMargin() > && (in_array('ROLE_B2B_ADMIN'$user->getRoles()) || in_array('ROLE_B2B_AGENT'$user->getRoles()))) {
  156.                 $showResellingRates true;
  157.             }
  158.         }
  159.         $requestJson json_decode($request->getContent(), true);
  160.         $xmlSourceId $request->headers->get('xmlSourceId') ?? 0;
  161.         $base_uri $request->getSchemeAndHttpHost();
  162.         // ----- PERFORM the HttpRequestCall ----- //
  163.         $data $hotelApiService->query_ApiHotel_Availability($auth_data$requestJson$xmlSourceId$base_uritrue);
  164.         $today DateTime::createFromFormat("Y-m-d"date("Y-m-d"));
  165.         if (count($data['content']) > 0) { // Re-format data to fit the FRONT-B2C :::: VIEW_BY_BOARD ::::
  166.             $nbRooms count($requestJson['occupancies']);
  167.             $content_front = []; // prepare data to fit the front design
  168.             foreach ($data['content'] as $hotel) {
  169.                 $hotel_key strtoupper($hotel['hotel']['name']); // HOTEL_ITEM_IDENTIFIER TO GROUP MULTIPLE HOTELS WITH SAME NAME
  170.                 $hotel_offer_count 0;
  171.                 if (isset($content_front[$hotel_key]['offersCount'])) {
  172.                     $hotel_offer_count $content_front[$hotel_key]['offersCount'];
  173.                 }
  174.                 $content_front[$hotel_key]['hotel'] = $hotel['hotel'];
  175.                 $content_front[$hotel_key]['hotel']['customRating'] = $this->hotelApiService->getHotelCustomRating($hotel['hotel']['name']);
  176.                 // TODO Add function getReviewRating(hotelName, reviewsSource);
  177.                 $content_front[$hotel_key]['hotel']['reviewRating'] = rand(410); // TODO GET RATING FROM External src like Tripadvisor
  178.                 if (count($hotel['sources']) > 0) {
  179.                     $content_front[$hotel_key]['hotel']['facilities'] = $hotel['sources'][0]['facilities'];
  180.                 }
  181.                 $content_front[$hotel_key]['hotel']['promos'] = [];
  182.                 $content_front[$hotel_key]['hotel']['discounts'] = [];
  183.                 $content_front[$hotel_key]['hotel']['infos'] = [];
  184.                 foreach ($hotel['sources'] as $hotel_source) {
  185.                     $sourceKey $hotel['hotel']['id']; // considering the full-hotel-id as the sourceKey (string containing the end-source-id)
  186.                     $content_front[$hotel_key]['sources'][$sourceKey]['sourceKey'] = $sourceKey;
  187.                     $hotelSourcePrice 0;
  188.                     $hotelSourceAvailable false;
  189.                     $hotelSourceFreeCancellation false;
  190.                     $tab_boards = [];
  191.                     foreach ($hotel_source['rooms'] as $index => $index_hrts) { // room_index
  192.                         // SORT hrts for current hotel-source by salePromoRate
  193.                         usort($index_hrts, function ($a$b) {
  194.                             return $a['salePromoRate'] <=> $b['salePromoRate']; // Ascending
  195.                         });
  196.                         foreach ($index_hrts as $hrt) {
  197.                             // freeCancellation FLAG to use in React Filter (i.e. is there a possibility to cancel without fees )
  198.                             if ($hrt['NRF']) {
  199.                                 $hrt['freeCancellation'] = false;
  200.                             } else {
  201.                                 if (is_null($hrt['deadline'])) {
  202.                                     $hrt['freeCancellation'] = true;
  203.                                 } else {
  204.                                     $deadline = new DateTime($hrt['deadline']);
  205.                                     $difference date_diff($today$deadline); // Interval before deadline.
  206.                                     if ($difference->invert) { // deadline is exceeded
  207.                                         $hrt['freeCancellation'] = false;
  208.                                     } else {
  209.                                         $hrt['freeCancellation'] = true;
  210.                                     }
  211.                                 }
  212.                             }
  213.                             $hotelSourceFreeCancellation $hotelSourceFreeCancellation || $hrt['freeCancellation'];
  214.                             // END freeCancellation FLAG
  215.                             // SHOW SaleResellingRates rather thant saleRate applied to the customer
  216.                             if ($showResellingRates) {
  217.                                 $hrt['salePromoRate'] = $hrt['salePromoRate'] * (100.0 $customer->getResellerMargin()) / 100.0;
  218.                                 $hrt['saleRate'] = $hrt['saleRate'] * (100.0 $customer->getResellerMargin()) / 100.0;
  219.                             }
  220.                             $content_front[$hotel_key]['sources'][$sourceKey]['hrts'][$hrt['boardName']][$index][] = $hrt;
  221.                             $tab_boards[$hrt['boardName']] = $hrt['boardXmlName'];
  222.                             // Increment the number of available-offers
  223.                             if ($hrt['available']) {
  224.                                 $hotel_offer_count++;
  225.                                 $hotelSourceAvailable true;
  226.                             }
  227.                         }
  228.                     } // END Room Indexes
  229.                     // HOTEL BADGES (Promos, Discounts, Infos)
  230.                     foreach ($hotel_source['promos'] as $item) {
  231.                         $content_front[$hotel_key]['hotel']['promos'][] = $item;
  232.                     }
  233.                     foreach ($hotel_source['discounts'] as $item) {
  234.                         $content_front[$hotel_key]['hotel']['discounts'][] = $item;
  235.                     }
  236.                     foreach ($hotel_source['infos'] as $item) {
  237.                         $content_front[$hotel_key]['hotel']['infos'][] = $item;
  238.                     }
  239.                     $bestOffers $this->hotelApiService->getBestPriceBoard($content_front[$hotel_key], $sourceKey);
  240.                     $content_front[$hotel_key]['sources'][$sourceKey]['bestPrice'] = $bestOffers['bestPrice_currentSourceKey'];
  241.                     $content_front[$hotel_key]['bestPrice'] = $bestOffers['bestPrice'];
  242.                     $content_front[$hotel_key]['bestBoard'] = $bestOffers['bestBoard'];
  243.                     $content_front[$hotel_key]['bestRooms'] = $this->helpers->formatArrayCounts($bestOffers['bestRooms']);
  244.                     $content_front[$hotel_key]['offersCount'] = $hotel_offer_count;
  245.                     foreach ($content_front[$hotel_key]['sources'][$sourceKey]['hrts'] as $board_key => $board_hrts) {
  246.                         if (count($board_hrts) < $nbRooms) {
  247.                             unset($content_front[$hotel_key]['sources'][$sourceKey]['hrts'][$board_key]);
  248.                         }
  249.                     }
  250.                     foreach ($tab_boards as $board_key => $board_value) {
  251.                         $content_front[$hotel_key]['sources'][$sourceKey]['boards'][] =
  252.                             [
  253.                                 'key' => $board_value,
  254.                                 'value' => $board_value,
  255.                             ];
  256.                     }
  257.                     // set general stock
  258.                     $content_front[$hotel_key]['sources'][$sourceKey]['generalStock'] = $hotel_source['generalStock'];
  259.                     // set search code
  260.                     $content_front[$hotel_key]['sources'][$sourceKey]['searchCode'] = $hotel_source['searchCode'];
  261.                     // set source id
  262.                     $content_front[$hotel_key]['sources'][$sourceKey]['xmlSourceId'] = $hotel_source['xmlSourceId'];
  263.                     // set xml source name
  264.                     $content_front[$hotel_key]['sources'][$sourceKey]['xmlSourceName'] = $hotel_source['xmlSourceName'];
  265.                     // set hotel Source available
  266.                     $content_front[$hotel_key]['sources'][$sourceKey]['available'] = $hotelSourceAvailable;
  267.                     // set hotel Source available
  268.                     $content_front[$hotel_key]['sources'][$sourceKey]['freeCancellation'] = $hotelSourceFreeCancellation;
  269.                     // set hotel Source available
  270.                     $content_front[$hotel_key]['sources'][$sourceKey]['associationRequired'] = $hotel_source['associationRequired'];
  271.                 }
  272.             }
  273.             // array values hotels
  274.             $data['content'] = array_values($content_front);
  275.             // array values sources
  276.             foreach ($data['content'] as &$hotel) {
  277.                 usort($hotel['sources'], function ($a$b) {
  278.                     return $a['bestPrice'] <=> $b['bestPrice']; // Ascending
  279.                 });
  280.                 $hotel['sources'] = array_values($hotel['sources']);
  281.                 foreach ($hotel['sources'] as &$source) {
  282.                     $source['hrts'] = array_values($source['hrts']);
  283.                 }
  284.             }
  285.         }
  286.         return $this->json($data);
  287.     }
  288.     #[Route('/autocomplete'name'hotel_autocomplete'methods: ['GET'])]
  289.     public function autoComplete(Request $request): JsonResponse
  290.     {
  291.         $query $request->query->get('query');
  292.         if ($query == null) {
  293.             return $this->json([]);
  294.         }
  295.         $query_trim trim($query);
  296.         $headers = [
  297.             'Accept' => 'application/json',
  298.             'Content-Type' => 'application/json',
  299.         ];
  300.         try {
  301.             $httpClient HttpClient::create();
  302.             $response $httpClient->request('GET'$request->getSchemeAndHttpHost() . '/api/hotel/autocomplete', [
  303.                 'headers' => $headers,
  304.                 'query' => ['query' => $query_trim]
  305.             ]);
  306.             $data json_decode($response->getContent(), true);
  307.             return $this->json($data['response']);
  308.         } catch (Exception $e) {
  309.             return $this->json(['error' => $e->getMessage()]);
  310.         }
  311.     }
  312.     /**
  313.      * The detail of selected offer from the front : hotel search results page. --- Check-Rate ---
  314.      * Supposed to be called either from "hotel/search or "orderline_add_room"
  315.      */
  316.     #[Route('/offer-detail'name'hotel_offer_detail'methods: ['GET''POST'])]
  317.     public function hotelOfferDetail(
  318.         Request             $request,
  319.         OrderLineRepository $orderLineRepository,
  320.         OrderRepository     $orderRepository,
  321.     ): Response{
  322.         $guestData = [];
  323.         if ($request->isMethod('POST')) {
  324.             $guestData = [
  325.                 'first_name' => $request->request->get('first_name'),
  326.                 'last_name' => $request->request->get('last_name'),
  327.                 'phone' => $request->request->get('phone'),
  328.                 'email' => $request->request->get('email'),
  329.             ];
  330.             $request->getSession()->set('guestData'$guestData);
  331.         }
  332.         $customer $this->apiAuthenticationService->getCustomer($this->getUser());
  333.         $auth_data $this->apiAuthenticationService->getAuthenticationArray($this->getUser());
  334.         $rooms_rate_keys = [];
  335.         for ($i 0$i $request->get('nbRooms'); $i++) {
  336.             $rooms_rate_keys[] = ["rateKey" => $request->get("room-" $i)];
  337.         }
  338.         $checkRateRequest = [
  339.             'rooms' => $rooms_rate_keys,
  340.             'searchCode' => $request->get('searchCode')
  341.         ];
  342.         $saleCurrency $this->currencyService->getSessionCurrency();
  343.         $product_fee $this->frontService->getProductFee(ModuleEnum::hotel->getValue(), $customer);
  344.         /** call api/hotel/checkrate */
  345.         $httpClient HttpClient::create();
  346.         $response $httpClient->request(
  347.             'POST'$request->getUriForPath('/') . 'api/hotel/checkrate',
  348.             [
  349.                 'headers' => [
  350.                     'Accept' => 'application/json',
  351.                     'Content-Type' => 'application/json',
  352.                     'currency' => $saleCurrency,
  353.                     'user' => $auth_data['user'],
  354.                     'timestamp' => $auth_data['timestamp'],
  355.                     'signature' => hash('sha256'$auth_data['signature'])
  356.                 ],
  357.                 'json' => $checkRateRequest
  358.             ]
  359.         );
  360.         $responseData json_decode($response->getContent(), true);
  361.         $searchCriteriaArray json_decode($request->get("searchCriteria"), true);
  362.         $twig_parameters = [
  363.             'searchCriteriaArray' => $searchCriteriaArray,
  364.             'searchCode' => null,
  365.             'checkRateData' => null,
  366.             "social_networks" => $this->frontService->getSocialNetworks(),
  367.             'menu_front' => $this->frontService->getFrontMenu(),
  368.             "currencies" => $this->frontService->getCurrencies(),
  369.             "agencies" => $this->frontService->getAgencies(),
  370.             "society" => $this->parameterService->getSocietyParameters(),
  371.             "referenceCurrency" => $this->parameterService->getReferenceCurrency(),
  372.             'guestData' => $guestData,
  373.             'action' => '',
  374.             'customer' => $customer
  375.         ];
  376.         $template "";
  377.         if (is_null($responseData) or $responseData['error']) {
  378.             // CASE 0 : Template for Error
  379.             return $this->render("front/hotel/offer_error.html.twig"$twig_parameters);
  380.         } else {
  381.             $checkRateData reset($responseData['content']);
  382.             $checkRateData['available'] = $this->hotelApiService->isAvailable($checkRateData) ? "true" "false";
  383.             $rooms = [];
  384.             foreach ($checkRateData['sources'][0]['rooms'] as $room_index) {
  385.                 $rooms[] = $room_index[0]; // the selected room for the index.
  386.             }
  387.             //$checkRateData['cancellation'] = $this->hotelApiService->getDeadlineFromRooms($rooms);
  388.             $twig_parameters['checkRateData'] = $checkRateData;
  389.             $twig_parameters['searchCode'] = $responseData['searchCode'];
  390.             $twig_parameters['product_fee'] = $product_fee;
  391.             $twig_parameters['META_PIXEL_ID'] = $this->parameterService->getTrackerMetaId();
  392.             // CASE 1 : Template to add something to an existing orderline
  393.             if ($request->get('orderlineId')) {
  394.                 $orderlineID $request->get('orderlineId');
  395.                 $twig_parameters['orderLine'] = $orderLineRepository->find($orderlineID);
  396.                 $twig_parameters['action'] = $this->generateUrl("orderline_add_room", ['id' => $orderlineID]);
  397.             }
  398.             // CASE 2 : new Order and new OrderLine
  399.             if (empty($request->get('orderlineId')) && empty($request->get('orderId'))) { //
  400.                 $twig_parameters['action'] = $this->generateUrl("app_admin_add_cart_line_hotel");
  401.             }
  402.             // CASE 3 : Add new OrderLine into an existing Order
  403.             if (empty($request->get('orderlineId')) && $request->get('orderId')) { //
  404.                 $orderID $request->get('orderId');
  405.                 $twig_parameters['order'] = $orderRepository->find($orderID);
  406.                 $twig_parameters['action'] = $this->generateUrl("order_add_orderline_hotel_book", ['id' => $orderID]);
  407.             }
  408.             return $this->render("front/hotel/offer_detail.html.twig"$twig_parameters);
  409.         }
  410.     }
  411.     /**
  412.      *  The detail of a given hotelId
  413.      */
  414.     #[Route('/{country_label}/{city_label}/{hotel_label}/{id}'name'hotel_detail'methods: ['GET'])]
  415.     public function hotelDetail(
  416.         Request             $request,
  417.                             $id,
  418.         string              $city_label,
  419.         string              $hotel_label,
  420.         ParameterService    $parameterService,
  421.         HotelXmlRepository  $hotelXmlRepository
  422.     ): Response {
  423.         $hotel_id_elements explode("|"$id);
  424.         $xmlSourceId $hotel_id_elements[0];
  425.         //        dd($xmlSourceId);
  426.         $codeHotel $hotel_id_elements[1];
  427.         if ($xmlSourceId == 0) {
  428.             $hotelDetails $this->hotelApiService->hotelDetails($codeHotel$request->getLocale());
  429.         } else {
  430.             $hotelDetails $this->hotelApiService->hotelDetailsHUB($id);
  431.         }
  432.         $recommendedHotels $hotelXmlRepository->getRecommendedHotels($city_label$id);
  433.         $currentDateTime = new DateTime();
  434.         $arrivalDate $currentDateTime->format('Y-m-d');//
  435.         $departureDate = new DateTime("tomorrow");
  436.         $departureDate $departureDate->format('Y-m-d');
  437.         return $this->render('front/hotel/hotel_detail.html.twig', [
  438.             'hotel_data' => $hotelDetails,
  439.             'society' => $this->parameterService->getSocietyParameters(),
  440.             'social_networks' => $this->frontService->getSocialNetworks(),
  441.             'currencies' => $this->frontService->getCurrencies(),
  442.             'agencies' => $this->frontService->getAgencies(),
  443.             'menu_front' => $this->frontService->getFrontMenu(),
  444.             'recommendedHotels' => $recommendedHotels,
  445.             "arrivalDate" => $arrivalDate,
  446.             "departureDate" => $departureDate,
  447.             "city_label" => $city_label,
  448.             "hotel_label" => $hotel_label,
  449.             "META_PIXEL_ID" => $this->parameterService->getTrackerMetaId(),
  450.             'faqs' => $this->frontService->getFaqs(FaqRouteEnum::HOTEL_DETAIL$id),
  451.         ]);
  452.     }
  453.     #[Route('/{id}/gallery'name'hotel_gallery_json'methods: ['GET'])]
  454.     public function hotelGallery(
  455.         Request $request,
  456.                 $id
  457.     ): JsonResponse{
  458.         $auth_data $this->apiAuthenticationService->getAuthenticationArray($this->getUser());
  459.         // call hotel detail and get images
  460.         $hotelDetailsRequest = [
  461.             'hotelId' => $id
  462.         ];
  463.         $httpClient HttpClient::create();
  464.         $response $httpClient->request(
  465.             'POST'$request->getUriForPath('/') . 'api/hotel/details',
  466.             [
  467.                 'headers' => [
  468.                     'Accept' => 'application/json',
  469.                     'Content-Type' => 'application/json',
  470.                     'user' => $auth_data['user'],
  471.                     'timestamp' => $auth_data['timestamp'],
  472.                     'signature' => hash('sha256'$auth_data['signature'])
  473.                 ],
  474.                 'json' => $hotelDetailsRequest
  475.             ]
  476.         );
  477.         $responseData json_decode($response->getContent(), true);
  478.         if (is_null($responseData) || !key_exists('content'$responseData)) {
  479.             return $this->json([]);
  480.         }
  481.         $hotel_data_array $responseData['content']['images'];
  482.         return $this->json($hotel_data_array);
  483.     }
  484.     /**
  485.      * @throws Exception
  486.      */
  487.     #[Route('/promo'name'app_front_hotel_promo'methods: ['GET'])]
  488.     public function hotelPromo(HotelXmlPriceRepository $hotelXmlPriceRepository,
  489.         FrontService $frontService
  490.     ): Response{
  491.         $hotels = [];
  492.         $hotelXmlPricesWithPromos $hotelXmlPriceRepository->getHotelXmlPriceWithPromos();
  493.         foreach ($hotelXmlPricesWithPromos as $hotelXmlPricesWithPromo) {
  494.             $hotels[] = $frontService->getItemHotel($hotelXmlPricesWithPromo);
  495.         }
  496.         return $this->render('front/hotel/hotel_promo.html.twig', [
  497.             'hotelsWithPromos' => $hotels,
  498.             'social_networks' => $this->frontService->getSocialNetworks(),
  499.             'currencies' => $this->frontService->getCurrencies(),
  500.             'agencies' => $this->frontService->getAgencies(),
  501.             'society' => $this->parameterService->getSocietyParameters(),
  502.             'currencySwitcher' => true,
  503.             'META_PIXEL_ID' => $this->parameterService->getTrackerMetaId()
  504.         ]);
  505.     }
  506.     #[Route('/cart/addProductHotel'name'app_admin_add_cart_line_hotel'methods: ['POST'])]
  507.     public function addProductHotel(
  508.         Request             $request,
  509.         SessionInterface    $session,
  510.         CartService         $cartService,
  511.     ): RedirectResponse{
  512.         $cart_array $cartService->initCart($this->getUser(), $request);
  513.         /* 2- add a line into cart_array['products'] */
  514.         $nbRooms $request->get("nbRooms"); // quantity : nbRooms
  515.         $cart_line_label $request->get('product_label');
  516.         $cart_line_date $request->get('product_date');
  517.         $beneficiaryName $request->get('beneficiary_name');
  518.         $beneficiaryEmail $request->get('beneficiary_email');
  519.         $product_price $request->get("totalAmount");
  520.         $productFee $request->get('product_fee') ?? 0;
  521.         $dates explode('-'$cart_line_date);
  522.         $date1 DateTime::createFromFormat('d/m/Y'$dates[0]);
  523.         $date2 DateTime::createFromFormat('d/m/Y'$dates[1]);
  524.         $interval $date1->diff($date2);
  525.         $nbNights $interval->days;
  526.         $hotel_with_party false;
  527.         $product_elements null;
  528.         for ($room_idx 0$room_idx $nbRooms$room_idx++) {
  529.             /* guests */
  530.             $room_guests = [];
  531.             $room_adults_count $request->get("room_" $room_idx "_adults_count");
  532.             $room_children_count $request->get("room_" $room_idx "_children_count");
  533.             $room_guest_count $room_adults_count $room_children_count;
  534.             for ($guest_index 0$guest_index $room_guest_count$guest_index++) {
  535.                 $paxCivility $request->get("room_" $room_idx "_pax_" $guest_index "_civility");
  536.                 $paxFirstName $request->get("room_" $room_idx "_pax_" $guest_index "_firstName");
  537.                 $paxLastName $request->get("room_" $room_idx "_pax_" $guest_index "_lastName");
  538.                 $paxAge $request->get("room_" $room_idx "_pax_" $guest_index "_age");
  539.                 if (!empty($paxFirstName) || !empty($paxLastName)) {
  540.                     // Use the detailed per-pax data
  541.                     $guest = [
  542.                         'civility' => $paxCivility ?? '',
  543.                         'firstName' => $paxFirstName ?? '',
  544.                         'lastName' => $paxLastName ?? '',
  545.                         'age' => $paxAge,
  546.                     ];
  547.                 } else {
  548.                     // Fallback: use the primary beneficiary name (backward compatibility)
  549.                     $guest = [
  550.                         'civility' => '',
  551.                         'firstName' => $beneficiaryName,
  552.                         'lastName' => '-',
  553.                     ];
  554.                 }
  555.                 $room_guests[] = $guest;
  556.             }
  557.             $room = [
  558.                 'rateKey' => $request->get("room_" $room_idx "_key"),
  559.                 'label' => $request->get("room_" $room_idx "_label"),
  560.                 'isAvailable' => $request->get("room_" $room_idx "_available"),
  561.                 'price' => $request->get("room_" $room_idx "_price"),
  562.                 'adultsCount' => $room_adults_count,
  563.                 'childrenCount' => $room_children_count,
  564.                 'guests' => $room_guests
  565.             ];
  566.             $room_nb_parties $request->get("room_" $room_idx "_nbParties");
  567.             $room_parties_zones = [];
  568.             if (!is_null($room_nb_parties)) { // if there is parties associated to the current room
  569.                 $hotel_with_party true;
  570.                 for ($party_index 0$party_index $room_nb_parties$party_index++) {
  571.                     $room_parties_zones[] = $request->get("room_" $room_idx "_party_" $party_index);
  572.                 }
  573.                 $room['parties'] = $room_parties_zones;
  574.                 //$product_price+= $request->get("partyPrice_".$party_index);
  575.             }
  576.             $product_elements[] = $room;
  577.         }
  578.         if ($request->isMethod('POST')) {
  579.             $guestData = [
  580.                 'first_name' => $request->request->get('first_name'),
  581.                 'last_name' => $request->request->get('last_name'),
  582.                 'phone' => $request->request->get('phone'),
  583.                 'email' => $request->request->get('email'),
  584.             ];
  585.             $request->getSession()->set('guestData'$guestData);
  586.         }
  587.         $cart_line_hotel = [
  588.             'module' => ModuleEnum::hotel->getValue(),
  589.             'elements' => $product_elements,
  590.             'label' => $cart_line_label,
  591.             'date' => $cart_line_date,
  592.             'checkIn' => $dates[0],
  593.             'checkOut' => $dates[1],
  594.             'nbNights' => $nbNights,
  595.             'quantity' => $nbRooms,
  596.             'options' => $request->get("options"),
  597.             'comment' => $request->get("comment"),
  598.             'price' => $product_price,
  599.             'fee' => $productFee,
  600.             'available' => $request->get('available'),
  601.             'searchCode' => $request->get('searchCode'),
  602.             'tokenForBook' => $request->get('tokenForBook'),
  603.             'beneficiary' => [
  604.                 'name' => $beneficiaryName,
  605.                 'email' => $beneficiaryEmail
  606.             ],
  607.             'image' => $request->get('hotel_image'),
  608.             'city' => $request->get('city'),
  609.             'country' => $request->get('country'),
  610.             'rating' => $request->get('rating'),
  611.             'guestData' => $guestData,
  612.         ];
  613.         $cart_array['products'][] = $cart_line_hotel// add a cart line ( a product )
  614.         $session->set("cart"$cart_array);
  615.         //return $this->redirectToRoute('app_shared_cart_index', [], Response::HTTP_SEE_OTHER);
  616.         return $this->redirectToRoute('app_shared_cart_hotel_product_index', [], Response::HTTP_SEE_OTHER);
  617.     }
  618. }