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

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