src/Controller/PanierController.php line 125

Open in your IDE?
  1. <?php
  2. namespace App\Controller;
  3. use App\Entity\Produit;
  4. use App\Entity\Commande;
  5. use App\Entity\ProduitVariant;
  6. use App\Service\PanierService;
  7. use App\Entity\CommandeDetails;
  8. use App\Repository\ProduitRepository;
  9. use Doctrine\ORM\EntityManagerInterface;
  10. use App\Repository\ProduitVariantRepository;
  11. use Symfony\Component\HttpFoundation\Request;
  12. use Symfony\Component\HttpFoundation\Response;
  13. use Symfony\Component\HttpFoundation\JsonResponse;
  14. use Symfony\Component\Routing\Annotation\Route;
  15. use Symfony\Component\Translation\LocaleSwitcher;
  16. use Symfony\Contracts\Translation\TranslatorInterface;
  17. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  18. use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
  19. class PanierController extends AbstractController
  20. {
  21.     public $panierService;
  22.     public function __construct(PanierService $panierService)
  23.     {
  24.         $this->panierService $panierService;
  25.     }
  26.     /**
  27.      * True only when the product's shop exists and is active.
  28.      */
  29.     private function shopIsActive($product): bool
  30.     {
  31.         try {
  32.             $shop method_exists($product'getMagasin') ? $product->getMagasin() : null;
  33.             return $shop && method_exists($shop'isActive') && $shop->isActive() === true;
  34.         } catch (\Throwable $e) {
  35.             return false;
  36.         }
  37.     }
  38.     /**
  39.      * Look up the delivery fee record for a shop + region via raw SQL.
  40.      * Returns ['fee' => float, 'can_deliver' => bool] or null if no record.
  41.      */
  42.     private function lookupDeliveryFee(EntityManagerInterface $emint $shopIdint $regionId): ?array
  43.     {
  44.         try {
  45.             $conn $em->getConnection();
  46.             $row null;
  47.             if (method_exists($conn'fetchAssociative')) {
  48.                 $row $conn->fetchAssociative(
  49.                     'SELECT fee, can_deliver FROM magasin_delivery_fee WHERE magasin_id = :m AND region_id = :r',
  50.                     ['m' => $shopId'r' => $regionId]
  51.                 );
  52.             } else {
  53.                 $stmt $conn->executeQuery(
  54.                     'SELECT fee, can_deliver FROM magasin_delivery_fee WHERE magasin_id = ? AND region_id = ?',
  55.                     [$shopId$regionId]
  56.                 );
  57.                 $row method_exists($stmt'fetchAssociative') ? $stmt->fetchAssociative() : $stmt->fetch();
  58.             }
  59.             if (!$row) return null;
  60.             return [
  61.                 'fee'         => isset($row['fee']) ? (float) $row['fee'] : 0.0,
  62.                 'can_deliver' => isset($row['can_deliver']) ? ((int) $row['can_deliver'] === 1) : false,
  63.             ];
  64.         } catch (\Throwable $e) {
  65.             error_log('[Julico lookupDeliveryFee] ' $e->getMessage());
  66.             return null;
  67.         }
  68.     }
  69.     private function unitPriceOf($product): float
  70.     {
  71.         try {
  72.             $sp method_exists($product'getSalePrice') ? $product->getSalePrice() : null;
  73.             if ($sp !== null && (float) $sp 0) return (float) $sp;
  74.             $px method_exists($product'getPrix') ? $product->getPrix() : null;
  75.             if ($px !== null) return (float) $px;
  76.         } catch (\Throwable $e) {}
  77.         return 0.0;
  78.     }
  79.     /**
  80.      * Price for a cart line: the VARIANT's own price when a variant is selected,
  81.      * otherwise the product's (sale) price.
  82.      */
  83.     private function unitPriceOfLine($product$variant): float
  84.     {
  85.         try {
  86.             if ($variant !== null && method_exists($variant'getPrix')) {
  87.                 $vp $variant->getPrix();
  88.                 if ($vp !== null && (float) $vp 0) {
  89.                     return (float) $vp;
  90.                 }
  91.             }
  92.         } catch (\Throwable $e) {}
  93.         return $this->unitPriceOf($product);
  94.     }
  95.     #[Route('/panier'name'app_panier')]
  96.     public function index(SessionInterface $sessionProduitRepository $productrepository
  97.     ProduitVariantRepository $productVariantRepository
  98.     ,TranslatorInterface $translator,LocaleSwitcher $localeSwitcher,Request $request
  99.     ,EntityManagerInterface $entityManager): Response
  100.     {
  101.         if ($request->query->has('_locale')) {
  102.             $locale $request->getSession()->get('_locale');
  103.             if($locale != null){
  104.                 $localeSwitcher->setLocale($locale);
  105.                 $translator->setLocale($locale);    
  106.             }
  107.         }
  108.         $panier $session->get('panier',[]);
  109.         // ── Build cart rows (skip products that were deleted) ──
  110.         $panierWithData = [];
  111.         foreach($panier as $pan){
  112.             $product $productrepository->find($pan["id"]);
  113.             if (!$product) continue;
  114.             // ── Skip products whose shop isn't active (not sellable) ──
  115.             if (!$this->shopIsActive($product)) { continue; }
  116.             $productVariant null;
  117.             if ($pan["idVariante"] != 'notdefined'){
  118.                 $productVariant $productVariantRepository->find($pan["idVariante"]);
  119.             }
  120.             $panierWithData[] = [
  121.                 'product'        => $product,
  122.                 'productVariant' => $productVariant,
  123.                 'quantity'       => (int) $pan["qtt"],
  124.             ];
  125.         }
  126.         // ── Resolve which address to calculate delivery against ──
  127.         $user $this->getUser();
  128.         $addresses        = [];
  129.         $selectedAddress  null;
  130.         $requestedAddrId  $request->query->get('addr');
  131.         if ($user && method_exists($user'getAddresses')) {
  132.             foreach ($user->getAddresses() as $a) { $addresses[] = $a; }
  133.             if ($requestedAddrId) {
  134.                 foreach ($addresses as $a) {
  135.                     if ((string) $a->getId() === (string) $requestedAddrId) { $selectedAddress $a; break; }
  136.                 }
  137.             }
  138.             if (!$selectedAddress) {
  139.                 foreach ($addresses as $a) {
  140.                     if (method_exists($a'isDefaultAddresse') && $a->isDefaultAddresse()) { $selectedAddress $a; break; }
  141.                 }
  142.             }
  143.             if (!$selectedAddress && count($addresses) > 0) {
  144.                 $selectedAddress $addresses[0];
  145.             }
  146.         }
  147.         $selectedRegion null;
  148.         if ($selectedAddress && method_exists($selectedAddress'getRegion')) {
  149.             try { $selectedRegion $selectedAddress->getRegion(); } catch (\Throwable $e) {}
  150.         }
  151.         $selectedRegionId   = ($selectedRegion && method_exists($selectedRegion'getId')) ? (int) $selectedRegion->getId() : null;
  152.         $selectedRegionName = ($selectedRegion && method_exists($selectedRegion'getName')) ? $selectedRegion->getName() : null;
  153.         $deliveryResolvable = ($selectedRegionId !== null);
  154.         // ── Group items by shop ──
  155.         $groups   = [];
  156.         $subtotal 0.0;
  157.         foreach ($panierWithData as $row) {
  158.             $product   $row['product'];
  159.             $variant   $row['productVariant'];
  160.             $qty       $row['quantity'];
  161.             $unitPrice $this->unitPriceOfLine($product$variant);   // ← variant price if present
  162.             $lineTotal $unitPrice $qty;
  163.             $subtotal += $lineTotal;
  164.             $shop     method_exists($product'getMagasin') ? $product->getMagasin() : null;
  165.             $shopId   = ($shop && method_exists($shop'getId'))  ? (int) $shop->getId()  : 0;
  166.             $shopName = ($shop && method_exists($shop'getNom')) ? $shop->getNom()        : 'Shop';
  167.             if (!isset($groups[$shopId])) {
  168.                 $groups[$shopId] = [
  169.                     'shopId'      => $shopId,
  170.                     'shopName'    => $shopName,
  171.                     'items'       => [],
  172.                     'subtotal'    => 0.0,
  173.                     'canDeliver'  => null,
  174.                     'deliveryFee' => null,
  175.                     'feeKnown'    => false,
  176.                 ];
  177.             }
  178.             $groups[$shopId]['items'][] = [
  179.                 'product'        => $product,
  180.                 'productVariant' => $variant,
  181.                 'quantity'       => $qty,
  182.                 'unitPrice'      => $unitPrice,
  183.                 'lineTotal'      => $lineTotal,
  184.             ];
  185.             $groups[$shopId]['subtotal'] += $lineTotal;
  186.         }
  187.         // ── Per-shop delivery calculation ──
  188.         $totalDelivery    0.0;
  189.         $hasUndeliverable false;
  190.         foreach ($groups as $sid => &$g) {
  191.             if (!$deliveryResolvable) {
  192.                 $g['canDeliver'] = null$g['deliveryFee'] = null$g['feeKnown'] = false;
  193.                 continue;
  194.             }
  195.             $feeData = ($sid 0) ? $this->lookupDeliveryFee($entityManager$sid$selectedRegionId) : null;
  196.             if ($feeData === null) {
  197.                 $g['canDeliver'] = false$g['deliveryFee'] = null$g['feeKnown'] = false;
  198.                 $hasUndeliverable true;
  199.             } else {
  200.                 $g['canDeliver']  = $feeData['can_deliver'];
  201.                 $g['deliveryFee'] = $feeData['fee'];
  202.                 $g['feeKnown']    = true;
  203.                 if ($feeData['can_deliver']) {
  204.                     $totalDelivery += $feeData['fee'];
  205.                 } else {
  206.                     $hasUndeliverable true;
  207.                 }
  208.             }
  209.         }
  210.         unset($g);
  211.         $grandTotal  $subtotal $totalDelivery;
  212.         $canCheckout = ($user !== null)
  213.                     && ($selectedAddress !== null)
  214.                     && $deliveryResolvable
  215.                     && !$hasUndeliverable
  216.                     && count($groups) > 0;
  217.         return $this->render('panier/index.html.twig', [
  218.             'shopGroups'         => array_values($groups),
  219.             'addresses'          => $addresses,
  220.             'selectedAddress'    => $selectedAddress,
  221.             'selectedAddressId'  => $selectedAddress $selectedAddress->getId() : null,
  222.             'selectedRegionName' => $selectedRegionName,
  223.             'subtotal'           => $subtotal,
  224.             'totalDelivery'      => $totalDelivery,
  225.             'grandTotal'         => $grandTotal,
  226.             'hasUndeliverable'   => $hasUndeliverable,
  227.             'deliveryResolvable' => $deliveryResolvable,
  228.             'canCheckout'        => $canCheckout,
  229.             'itemCount'          => count($panierWithData),
  230.             'isLoggedIn'         => $user !== null,
  231.         ]);
  232.     }
  233.     
  234.     #[Route('/panier/add/{id}/{idVariante}/{option}'name'panier_add')]
  235.     public function add($id$idVariante$optionSessionInterface $sessionRequest $request,
  236.         EntityManagerInterface $entityManagerTranslatorInterface $translator,
  237.         LocaleSwitcher $localeSwitcher): Response
  238.     {
  239.         if (empty($idVariante)) { $idVariante 'notdefined'; }
  240.         if (!isset($idVariante)) { $idVariante 'notdefined'; }
  241.         if ($request->query->has('_locale')) {
  242.             $locale $request->getSession()->get('_locale');
  243.             if($locale != null){
  244.                 $localeSwitcher->setLocale($locale);
  245.                 $translator->setLocale($locale);    
  246.             }
  247.         }
  248.         // ── Check if AJAX request ──
  249.         $isAjax $request->isXmlHttpRequest()
  250.                || $request->headers->get('X-Requested-With') === 'XMLHttpRequest'
  251.                || $request->headers->get('Accept') === 'application/json';
  252.         $srv_msg = [];
  253.         $panier  $session->get('panier', []);
  254.         $qtt     $request->request->get('qttprdid') ?? 1;
  255.         $size    $request->request->get('chosenSize');
  256.         $color   $request->request->get('chosenColor');
  257.         
  258.         if ($id != null){
  259.             $produit $entityManager->getRepository(Produit::class)->find($id);
  260.             // ── Block ordering from a shop that isn't active yet ──
  261.             if ($produit !== null && !$this->shopIsActive($produit)) {
  262.                 $msg "This shop isn't active yet — its products can't be ordered.";
  263.                 $session->set('srv_msg'$msg);
  264.                 if ($isAjax) {
  265.                     return new JsonResponse(['success' => false'message' => $msg], 403);
  266.                 }
  267.                 return $this->redirectToRoute("show_produit", ['id' => $id]);
  268.             }
  269.             $stock   $produit->getQtt();
  270.             if($stock !== null && $stock $qtt){
  271.                 $srv_msg = ["data" => "Available Qty $stock"];
  272.                 $session->set('srv_msg'$srv_msg["data"]);
  273.                 if ($isAjax) {
  274.                     return new JsonResponse(['success' => false'message' => "Only $stock available"], 400);
  275.                 }
  276.                 return $this->redirectToRoute("show_produit", ['id' => $id]);
  277.             }
  278.             if ($idVariante == 'notdefined'){
  279.                 if($size != null || $color != null){
  280.                     $variant $entityManager->getRepository(ProduitVariant::class)
  281.                         ->findProductByColorAndSizeAndProductId($id$color$size);
  282.                     if ($variant != null){
  283.                         $idVariante $variant->getId(); 
  284.                         $stock      $variant->getQtt();
  285.                     } else {
  286.                         $stock 0;
  287.                     }
  288.                 }
  289.             } else {
  290.                 $variant $entityManager->getRepository(ProduitVariant::class)->find($idVariante);
  291.                 if ($variant != null){
  292.                     $stock $variant->getQtt();
  293.                 } else {
  294.                     $stock 0;
  295.                 }
  296.             }
  297.             if($stock !== null && $stock $qtt){
  298.                 $srv_msg = ["data" => "Available Qty $stock"];
  299.                 $session->set('srv_msg'$srv_msg["data"]);
  300.                 if ($isAjax) {
  301.                     return new JsonResponse(['success' => false'message' => "Only $stock available"], 400);
  302.                 }
  303.                 return $this->redirectToRoute("show_produit", ['id' => $id]);
  304.             }
  305.         }
  306.         if(count($panier) == 0){
  307.             $session->set('duration'240);
  308.             $session->set('start_time'date("Y-m-d H:i:s"));
  309.             $end_time date('Y-m-d H:i:s',
  310.                 strtotime('+' $session->get('duration') . 'minutes',
  311.                 strtotime($session->get('start_time'))));
  312.             $session->set('end_time'$end_time);
  313.         }    
  314.         try {
  315.             $panier $session->get('panier', []);
  316.             if ($this->panierService->produit_existe_dans_panier($panier$id$idVariante)) {
  317.                 $this->panierService->modifierQuantite($session$id$idVariante$qtt);
  318.             } else {
  319.                 $this->panierService->ajouterAuPanier($session$id$idVariante$qtt);
  320.             }
  321.             // ── Return JSON for AJAX calls ──
  322.             if ($isAjax) {
  323.                 $newPanier $session->get('panier', []);
  324.                 $count array_sum(array_column($newPanier'qtt'));
  325.                 return new JsonResponse(['success' => true'cartCount' => $count]);
  326.             }
  327.             if ($option == "done"){
  328.                 return $this->redirectToRoute("app_panier");
  329.             }
  330.                 
  331.         } catch(\Exception $e) {
  332.             if ($isAjax) {
  333.                 return new JsonResponse(['success' => false'message' => 'Error'], 500);
  334.             }
  335.             return $this->redirectToRoute("app_home");
  336.         }
  337.         return $this->redirectToRoute("app_home");
  338.     }
  339.     // ══════════════════════════════════════════════════════
  340.     //  REMOVE ITEM FROM CART
  341.     // ══════════════════════════════════════════════════════
  342.     #[Route('/panier/remove/{id}/{idVariante}'name'panier_remove'methods: ['POST''GET'])]
  343.     public function remove($id$idVarianteSessionInterface $sessionRequest $request): Response
  344.     {
  345.         if (empty($idVariante) || !isset($idVariante)) {
  346.             $idVariante 'notdefined';
  347.         }
  348.         $isAjax $request->isXmlHttpRequest()
  349.                || $request->headers->get('X-Requested-With') === 'XMLHttpRequest'
  350.                || $request->headers->get('Accept') === 'application/json';
  351.         $panier    $session->get('panier', []);
  352.         $newPanier = [];
  353.         $removed   false;
  354.         foreach ($panier as $item) {
  355.             $itemId  = isset($item['id'])         ? (string)$item['id']         : '';
  356.             $itemVar = isset($item['idVariante']) ? (string)$item['idVariante'] : 'notdefined';
  357.             if ($itemId === (string)$id && $itemVar === (string)$idVariante) {
  358.                 $removed true;
  359.                 continue;
  360.             }
  361.             $newPanier[] = $item;
  362.         }
  363.         $session->set('panier'array_values($newPanier));
  364.         if ($isAjax) {
  365.             $count array_sum(array_column($newPanier'qtt'));
  366.             return new JsonResponse([
  367.                 'success'   => true,
  368.                 'removed'   => $removed,
  369.                 'cartCount' => $count,
  370.                 'isEmpty'   => count($newPanier) === 0,
  371.             ]);
  372.         }
  373.         return $this->redirectToRoute("app_panier");
  374.     }
  375.     #[Route('/panier/minus/{id}/{idVariante}/{option}'name'panier_minus')]
  376.     public function minus($id$idVariante$optionSessionInterface $sessionRequest $request): Response
  377.     {
  378.         if (empty($idVariante))  { $idVariante 'notdefined'; }
  379.         if (!isset($idVariante)) { $idVariante 'notdefined'; }
  380.         $isAjax $request->isXmlHttpRequest()
  381.                || $request->headers->get('X-Requested-With') === 'XMLHttpRequest';
  382.         try {
  383.             $this->panierService->supprimerDuPanier($session$id$idVariante);
  384.         } catch(\Exception $e) {}
  385.         if ($isAjax) {
  386.             $newPanier $session->get('panier', []);
  387.             $count     array_sum(array_column($newPanier'qtt'));
  388.             return new JsonResponse(['success' => true'cartCount' => $count]);
  389.         }
  390.         return $this->redirectToRoute("app_panier");
  391.     }
  392. }