src/Controller/Blog/HomepageController.php line 311

Open in your IDE?
  1. <?php
  2. namespace App\Controller\Blog;
  3. use App\Entity\Core\Pricing;
  4. use App\Entity\Core\Promotion;
  5. use App\Entity\Core\Reservations;
  6. use App\Entity\Core\Season;
  7. use App\Entity\Pages\Pages;
  8. use App\Form\ReservationForm;
  9. use App\Services\Core\Translations;
  10. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  11. use Symfony\Component\HttpFoundation\RedirectResponse;
  12. use Symfony\Component\HttpFoundation\Request;
  13. use Symfony\Component\HttpFoundation\Response;
  14. use Doctrine\ORM\EntityManagerInterface;
  15. use Symfony\Contracts\HttpClient\HttpClientInterface;
  16. use Twig\Environment;
  17. class HomepageController extends AbstractController
  18. {
  19. private $em;
  20. private $httpClient;
  21. private $twig;
  22. private $ts;
  23. public function __construct(EntityManagerInterface $em, HttpClientInterface $httpClient, Translations $translationService, Environment $twig) {
  24. $this->em = $em;
  25. $this->httpClient = $httpClient;
  26. $this->ts = $translationService;
  27. $this->twig = $twig;
  28. }
  29. public function homepage(Request $request): Response
  30. {
  31. $locale = $request->getLocale();
  32. $page = $this->em->getRepository(Pages::class)->findOneBy(['name' => 'homepage', 'locale' => $locale]);
  33. if($page == null) {
  34. $page = $this->em->getRepository(Pages::class)->findOneBy(['name' => 'homepage','locale' => "en"]);
  35. }
  36. $pricingMatrix = $this->em->getRepository(Pricing::class)->getMatrix(Pricing::SEASON_LOW);
  37. $reservation = new Reservations();
  38. $form = $this->createForm(ReservationForm::class, $reservation);
  39. $form->handleRequest($request);
  40. if ($form->isSubmitted() && $form->isValid()) {
  41. $data = $request->request->all();
  42. $data = $data['reservation_form'];
  43. $adults = (int)($data['adults'] ?? 0);
  44. $children = (int)($data['children'] ?? 0);
  45. $rooms = (int)($data['rooms'] ?? 1);
  46. $persons = $adults + $children;
  47. $reservation->setAdults($adults);
  48. $reservation->setChildren($children);
  49. $reservation->setRooms($rooms);
  50. $reservation->setPersons($persons);
  51. $reservation->setLocale($locale);
  52. $this->em->persist($reservation);
  53. $this->em->flush();
  54. $response = $this->httpClient->request("POST", $_ENV["WEBHOOK_RESERVATION"], [
  55. 'json' => [
  56. 'reservation_id' => $reservation->getId(),
  57. 'start' => $data['start'],
  58. 'end' => $data['end'],
  59. 'rooms' => $rooms,
  60. 'adults' => $adults,
  61. 'children' => $children,
  62. 'persons' => $persons,
  63. ],
  64. 'headers' => ['Content-Type' => 'application/json']
  65. ]);
  66. $dataWebhook = json_decode($response->getContent());
  67. $dispo = ($dataWebhook->available == true) ? 1 : 0;
  68. if($dispo == 0) {
  69. if($locale == "fr") {
  70. return $this->redirectToRoute('locale_reservation_error');
  71. }
  72. return $this->redirectToRoute('reservation_error');
  73. }
  74. $result = $this->calculateStayPrice($data['start'], $data['end'], $rooms, $persons);
  75. if($result['nights'] == 1 || $result['total_price'] === null) {
  76. if($locale == "fr") {
  77. return $this->redirectToRoute('locale_reservation_error');
  78. }
  79. return $this->redirectToRoute('reservation_error');
  80. }
  81. $price = $result["total_price"];
  82. $reservation->setPricing($price);
  83. $this->em->persist($reservation);
  84. $this->em->flush();
  85. $responseStripe = $this->httpClient->request("POST", $_ENV["WEBHOOK_STRIPE"], [
  86. 'json' => [
  87. 'reservation_id' => (string)$reservation->getId(),
  88. 'start' => $data['start'],
  89. 'end' => $data['end'],
  90. 'price' => $price,
  91. 'rooms' => $rooms,
  92. 'adults' => $adults,
  93. 'children' => $children,
  94. 'persons' => $persons,
  95. 'low_nights' => $result['low_nights'],
  96. 'high_nights' => $result['high_nights'],
  97. 'promo_label' => $result['promo_label'],
  98. 'discount_amount' => $result['discount_amount'],
  99. 'locale' => $locale,
  100. ],
  101. 'headers' => ['Content-Type' => 'application/json']
  102. ]);
  103. $dataWebhookStripe = json_decode($responseStripe->getContent());
  104. return new RedirectResponse($dataWebhookStripe->url);
  105. }
  106. return $this->render('vitrine/'.$locale.'/homepage.html.twig', [
  107. 'form' => $form->createView(),
  108. 'page' => $page,
  109. 'pricingMatrix' => $pricingMatrix,
  110. ]);
  111. }
  112. public function reservationSuccess(Request $request): Response
  113. {
  114. $locale = $request->getLocale();
  115. return $this->render('vitrine/'.$locale.'/reservation_success.html.twig', []);
  116. }
  117. public function reservationError(Request $request): Response
  118. {
  119. $locale = $request->getLocale();
  120. return $this->render('vitrine/'.$locale.'/reservation_error.html.twig', []);
  121. }
  122. public function reservationCancel(Request $request): Response
  123. {
  124. $locale = $request->getLocale();
  125. return $this->render('vitrine/'.$locale.'/reservation_cancel.html.twig', []);
  126. }
  127. /**
  128. * Calcul du prix nuit par nuit + meilleure promotion applicable depuis la BDD
  129. */
  130. private function calculateStayPrice(string $startDate, string $endDate, int $rooms, int $persons): array
  131. {
  132. $start = new \DateTime($startDate);
  133. $end = new \DateTime($endDate);
  134. $nights = $start->diff($end)->days;
  135. if ($nights < 2) {
  136. return ['nights' => 1, 'total_price' => null];
  137. }
  138. $pricingRepo = $this->em->getRepository(Pricing::class);
  139. $seasonRepo = $this->em->getRepository(Season::class);
  140. $promotionRepo = $this->em->getRepository(Promotion::class);
  141. $seasons = $seasonRepo->getAllCached();
  142. $subtotalAll = 0.0;
  143. $lowNights = 0;
  144. $highNights = 0;
  145. $lowSubtotal = 0.0;
  146. $highSubtotal = 0.0;
  147. $cursor = clone $start;
  148. for ($i = 0; $i < $nights; $i++) {
  149. $isHigh = false;
  150. foreach ($seasons as $s) {
  151. if ($s->contains($cursor)) { $isHigh = true; break; }
  152. }
  153. $season = $isHigh ? Pricing::SEASON_HIGH : Pricing::SEASON_LOW;
  154. $pricePerNight = $pricingRepo->findPricePerNight($rooms, $persons, $season);
  155. if ($pricePerNight === null) {
  156. return [
  157. 'nights' => $nights,
  158. 'total_price' => null,
  159. 'invalid_combo' => true,
  160. ];
  161. }
  162. $subtotalAll += $pricePerNight;
  163. if ($isHigh) {
  164. $highNights++;
  165. $highSubtotal += $pricePerNight;
  166. } else {
  167. $lowNights++;
  168. $lowSubtotal += $pricePerNight;
  169. }
  170. $cursor->modify('+1 day');
  171. }
  172. // Application de la meilleure promotion (depuis la BDD)
  173. $best = $promotionRepo->findBestDiscount(
  174. $nights,
  175. $lowNights,
  176. $highNights,
  177. $subtotalAll,
  178. $lowSubtotal,
  179. $highSubtotal
  180. );
  181. $discountAmount = $best['discount_amount'];
  182. $promotion = $best['promotion'];
  183. $totalPrice = $subtotalAll - $discountAmount;
  184. return [
  185. 'total_price' => $totalPrice,
  186. 'subtotal' => $subtotalAll,
  187. 'discount_amount' => $discountAmount,
  188. 'promo_label' => $promotion ? $promotion->getLabel() : null,
  189. 'promo_percent' => $promotion ? $promotion->getDiscountPercent() : null,
  190. 'nights' => $nights,
  191. 'low_nights' => $lowNights,
  192. 'high_nights' => $highNights,
  193. 'low_subtotal' => $lowSubtotal,
  194. 'high_subtotal' => $highSubtotal,
  195. 'rooms' => $rooms,
  196. 'persons' => $persons,
  197. ];
  198. }
  199. public function simulationPricing(Request $request): Response
  200. {
  201. $locale = $request->getLocale();
  202. $start = $_POST['start'] ?? null;
  203. $end = $_POST['end'] ?? null;
  204. $rooms = (int)($_POST['rooms'] ?? 1);
  205. $adults = (int)($_POST['adults'] ?? 0);
  206. $children = (int)($_POST['children'] ?? 0);
  207. $persons = $adults + $children;
  208. if (!$start || !$end || $persons < 1) {
  209. return $this->render('vitrine/'.$locale.'/components/simulation.html.twig', [
  210. 'nights' => 0,
  211. ]);
  212. }
  213. // Check dispo tolérant aux pannes du webhook
  214. $dispo = 1;
  215. try {
  216. $response = $this->httpClient->request("POST", $_ENV["WEBHOOK_RESERVATION"], [
  217. 'json' => [
  218. 'reservation_id' => 0,
  219. 'start' => $start,
  220. 'end' => $end,
  221. 'rooms' => $rooms,
  222. 'adults' => $adults,
  223. 'children' => $children,
  224. 'persons' => $persons,
  225. 'locale' => $locale,
  226. ],
  227. 'headers' => ['Content-Type' => 'application/json'],
  228. 'timeout' => 5,
  229. ]);
  230. $dataWebhook = json_decode($response->getContent());
  231. $dispo = (isset($dataWebhook->available) && $dataWebhook->available == true) ? 1 : 0;
  232. } catch (\Throwable $e) {
  233. // Webhook indisponible — on continue
  234. }
  235. $result = $this->calculateStayPrice($start, $end, $rooms, $persons);
  236. if ($result['nights'] == 1) {
  237. return $this->render('vitrine/'.$locale.'/components/simulation.html.twig', [
  238. 'nights' => $result['nights'],
  239. ]);
  240. }
  241. return $this->render('vitrine/'.$locale.'/components/simulation.html.twig', [
  242. 'price' => $result['total_price'],
  243. 'subtotal' => $result['subtotal'] ?? null,
  244. 'discount_amount' => $result['discount_amount'] ?? 0,
  245. 'promo_label' => $result['promo_label'] ?? null,
  246. 'promo_percent' => $result['promo_percent'] ?? null,
  247. 'dispo' => $dispo,
  248. 'nights' => $result['nights'],
  249. 'low_nights' => $result['low_nights'] ?? 0,
  250. 'high_nights' => $result['high_nights'] ?? 0,
  251. 'low_subtotal' => $result['low_subtotal'] ?? 0,
  252. 'high_subtotal' => $result['high_subtotal'] ?? 0,
  253. 'rooms' => $rooms,
  254. 'adults' => $adults,
  255. 'children' => $children,
  256. 'persons' => $persons,
  257. 'invalid_combo' => $result['invalid_combo'] ?? false,
  258. ]);
  259. }
  260. public function mentions(Request $request): Response
  261. {
  262. $locale = $request->getLocale();
  263. return $this->render('vitrine/'.$locale.'/mentions.html.twig', []);
  264. }
  265. public function cgv(Request $request): Response
  266. {
  267. $locale = $request->getLocale();
  268. return $this->render('vitrine/'.$locale.'/cgv.html.twig', []);
  269. }
  270. }