src/ECommerceBundle/Controller/FrontEnd/CartWidgetController.php line 387

Open in your IDE?
  1. <?php
  2. namespace App\ECommerceBundle\Controller\FrontEnd;
  3. use App\BaseBundle\Controller\AbstractController;
  4. use App\CurrencyBundle\Service\GoogleAnalyticService;
  5. use App\ECommerceBundle\Entity\Cart;
  6. use App\ECommerceBundle\Entity\CartHasProductPrice;
  7. use App\ECommerceBundle\Repository\CartHasProductPriceRepository;
  8. use App\ECommerceBundle\Repository\CartRepository;
  9. use App\ECommerceBundle\Repository\CouponRepository;
  10. use App\ECommerceBundle\Service\CartService;
  11. use App\ECommerceBundle\Service\CouponService;
  12. use App\ECommerceBundle\Service\FacebookConversionAPIService;
  13. use App\NewShippingBundle\Entity\ShippingTime;
  14. use App\ProductBundle\DTO\CartProductDTO;
  15. use App\ProductBundle\Entity\ProductFavorite;
  16. use App\ProductBundle\Entity\ProductPrice;
  17. use App\ProductBundle\Repository\ProductFavoriteRepository;
  18. use App\ProductBundle\Repository\ProductPriceRepository;
  19. use App\UserBundle\Entity\User;
  20. use Doctrine\ORM\EntityManagerInterface;
  21. use PN\ServiceBundle\Utils\Date;
  22. use PN\ServiceBundle\Utils\Validate;
  23. use Symfony\Component\HttpFoundation\Request;
  24. use Symfony\Component\HttpFoundation\Response;
  25. use Symfony\Component\Routing\Annotation\Route;
  26. use Symfony\Contracts\Translation\TranslatorInterface;
  27. /**
  28. * @Route("/cart-widget")
  29. */
  30. class CartWidgetController extends AbstractController
  31. {
  32. private array $excludeAgents = [
  33. "google",
  34. "facebook",
  35. "yandex",
  36. "bing",
  37. "freshpingbot",
  38. "bot",
  39. ];
  40. private TranslatorInterface $translator;
  41. private CartRepository $cartRepository;
  42. private CartService $cartService;
  43. private CartProductDTO $cartProductDTO;
  44. public function __construct(
  45. EntityManagerInterface $em,
  46. TranslatorInterface $translator,
  47. CartRepository $cartRepository,
  48. CartService $cartService,
  49. CartProductDTO $cartProductDTO
  50. )
  51. {
  52. parent::__construct($em);
  53. $this->translator = $translator;
  54. $this->cartRepository = $cartRepository;
  55. $this->cartService = $cartService;
  56. $this->cartProductDTO = $cartProductDTO;
  57. }
  58. /**
  59. * @Route("/list-ajax", name="fe_cart_widget_list_ajax", methods={"GET"})
  60. */
  61. public function cart(Request $request): Response
  62. {
  63. $userAgent = $request->headers->get("User-Agent");
  64. foreach ($this->excludeAgents as $agent) {
  65. if (stripos($userAgent, $agent) !== false) {
  66. return $this->json(new \stdClass());
  67. }
  68. }
  69. $cart = $this->cartService->getCart(createCartIfNotExist: true);
  70. if (!$cart) {
  71. throw $this->createNotFoundException('Unable to find Cart entity.');
  72. }
  73. return $this->json($this->getCartObject($cart));
  74. }
  75. /**
  76. * @Route("/add-item-ajax", name="fe_cart_widget_add_item_ajax", methods={"POST"})
  77. */
  78. public function addItemAjax(
  79. Request $request,
  80. GoogleAnalyticService $googleAnalyticService,
  81. FacebookConversionAPIService $facebookConversionAPIService,
  82. ProductPriceRepository $productPriceRepository
  83. ): Response
  84. {
  85. $productPriceId = $request->request->get('productPriceId');
  86. $qty = $request->request->has('qty') ? $request->request->get('qty') : 1;
  87. if (!Validate::not_null($productPriceId)) {
  88. return $this->json(["error" => true, 'message' => "Please enter product price ID"]);
  89. }
  90. $cart = $this->cartService->getCart();
  91. if (!$cart instanceof Cart) {
  92. return $this->json(["error" => true, 'message' => "don't play with us"]);
  93. }
  94. $productPrice = $productPriceRepository->find($productPriceId);
  95. if (!$productPrice instanceof ProductPrice) {
  96. return $this->json(["error" => true, 'message' => "Invalid Product"]);
  97. }
  98. if ($productPrice->getUnitPrice() < 1) {
  99. return $this->json(["error" => true, 'message' => $this->translator->trans("no_stock_msg")]);
  100. }
  101. // if ($qty < $productPrice->getMinimumPurchaseQty()) {
  102. // $qty = $productPrice->getMinimumPurchaseQty();
  103. // }
  104. $cartHasProductPrice = $this->em()->getRepository(CartHasProductPrice::class)->findOneBy([
  105. 'cart' => $cart,
  106. 'productPrice' => $productPrice,
  107. ]);
  108. $newQty = (($cartHasProductPrice) ? $cartHasProductPrice->getQty() : 0) + $qty;
  109. if ($newQty > $productPrice->getStock()) {
  110. return $this->json(["error" => true, 'message' => $this->translator->trans("no_stock_msg")]);
  111. }
  112. if (!$cartHasProductPrice instanceof CartHasProductPrice) {
  113. $cartHasProductPrice = new CartHasProductPrice();
  114. $cartHasProductPrice->setCart($cart);
  115. $cartHasProductPrice->setProductPrice($productPrice);
  116. $cart->addCartHasProductPrice($cartHasProductPrice);
  117. }
  118. $cartHasProductPrice->setQty($newQty);
  119. $cart->setCreated(new \DateTime());
  120. $this->em()->persist($cartHasProductPrice);
  121. $this->em()->flush();
  122. $this->cartService->initCart($cart);
  123. $facebookConversionAPIService->sendEventByCart($cart, FacebookConversionAPIService::EVENT_ADD_TO_CART);
  124. $gtmProductsObject = $googleAnalyticService->getProductObject($productPrice,
  125. $cartHasProductPrice->getQty());
  126. return $this->json([
  127. "error" => false,
  128. 'cart' => $this->getCartObject($cart),
  129. 'gtmProductsObjects' => $gtmProductsObject,
  130. ]);
  131. }
  132. /**
  133. * @Route("/remove-item-ajax", name="fe_cart_widget_remove_item_ajax", methods={"GET", "POST"})
  134. */
  135. public function removeItemAjax(
  136. Request $request,
  137. GoogleAnalyticService $googleAnalyticService,
  138. ProductPriceRepository $productPriceRepository,
  139. ): Response
  140. {
  141. $productPriceId = $request->get('productPriceId');
  142. if (!Validate::not_null($productPriceId)) {
  143. return $this->json(["error" => false, 'message' => "Please enter product price ID"]);
  144. }
  145. $cart = $this->cartService->getCart();
  146. if (!$cart instanceof Cart) {
  147. return $this->json(["error" => false, 'message' => "don't play with us"]);
  148. }
  149. $productPrice = $productPriceRepository->find($productPriceId);
  150. if (!$productPrice instanceof ProductPrice) {
  151. return $this->json(["error" => true, 'message' => "Invalid Product"]);
  152. }
  153. $cartHasProductPrice = $this->em()->getRepository(CartHasProductPrice::class)->findOneBy([
  154. 'cart' => $cart,
  155. 'productPrice' => $productPrice,
  156. ]);
  157. if ($cartHasProductPrice instanceof CartHasProductPrice) {
  158. $this->em()->remove($cartHasProductPrice);
  159. $this->em()->flush();
  160. }
  161. $this->cartService->initCart($cart);
  162. $gtmProductsObject = $googleAnalyticService->getProductObject(
  163. $productPrice,
  164. $cartHasProductPrice != null ? $cartHasProductPrice->getQty() : 0
  165. );
  166. return $this->json([
  167. "error" => false,
  168. 'cart' => $this->getCartObject($cart),
  169. 'gtmProductsObjects' => $gtmProductsObject,
  170. ]);
  171. }
  172. /**
  173. * @Route("/update-qty-item-ajax", name="fe_cart_widget_update_qty_item_ajax", methods={"POST"})
  174. */
  175. public function updateQtyItemAjax(
  176. Request $request,
  177. ProductPriceRepository $productPriceRepository,
  178. ): Response
  179. {
  180. $productPriceId = $request->get('productPriceId');
  181. $qty = $request->request->has('qty') ? $request->request->get('qty') : 1;
  182. if (!isset($qty)) {
  183. $qty = 1;
  184. }
  185. //Validation
  186. if (!is_numeric($qty)) {
  187. return $this->json(["error" => true, 'message' => 'Please enter a valid qty']);
  188. }
  189. if (!Validate::not_null($productPriceId)) {
  190. return $this->json(["error" => false, 'message' => "Please enter product price ID"]);
  191. }
  192. $cart = $this->cartService->getCart();
  193. $productPrice = $productPriceRepository->find($productPriceId);
  194. if (!$productPrice instanceof ProductPrice) {
  195. return $this->json(["error" => true, 'message' => "Invalid Product"]);
  196. }
  197. if ($productPrice->getStock() < $qty) {
  198. return $this->json(["error" => false, 'message' => $this->translator->trans("dont_have_require_qty_in_stock_msg")]);
  199. }
  200. $cartHasProductPrice = $this->em()->getRepository(CartHasProductPrice::class)->findOneBy([
  201. 'cart' => $cart,
  202. 'productPrice' => $productPrice,
  203. ]);
  204. if ($cartHasProductPrice instanceof CartHasProductPrice and $qty > 0) {
  205. $cartHasProductPrice->setQty($qty);
  206. $cart->setCreated(new \DateTime());
  207. $this->em()->persist($cartHasProductPrice);
  208. $this->em()->flush();
  209. $this->cartService->initCart($cart);
  210. }
  211. return $this->json([
  212. "error" => false,
  213. 'cart' => $this->getCartObject($cart),
  214. ]);
  215. }
  216. /**
  217. * @Route("/remove-from-cart-and-add-to-wishlist-ajax", name="fe_cart_widget_remove_from_cart_and_add_to_wishlist_ajax", methods={"POST"})
  218. */
  219. public function removeFromCartAndAddToWishlist(
  220. Request $request,
  221. GoogleAnalyticService $googleAnalyticService,
  222. CartHasProductPriceRepository $cartHasProductPriceRepository,
  223. ProductFavoriteRepository $productFavoriteRepository
  224. ): Response
  225. {
  226. $user = $this->getUser();
  227. if (!$user instanceof User) {
  228. return $this->json(["error" => false, "message" => $this->translator->trans("please_login_first_msg")]);
  229. }
  230. $cart = $this->cartService->getCart();
  231. $productPriceId = $request->request->get('productPriceId');
  232. $cartHasProductPrice = $cartHasProductPriceRepository->findOneBy([
  233. 'cart' => $cart,
  234. 'productPrice' => $productPriceId,
  235. ]);
  236. $gtmProductsObject = null;
  237. if ($cartHasProductPrice instanceof CartHasProductPrice) {
  238. $gtmProductsObject = $googleAnalyticService->getProductObject($cartHasProductPrice->getProductPrice(),
  239. $cartHasProductPrice->getQty());
  240. }
  241. $this->doRemoveFromCartAndAddToWishlist($productFavoriteRepository, $cartHasProductPrice);
  242. $this->cartService->initCart($cart);
  243. return $this->json([
  244. "error" => false,
  245. 'cart' => $this->getCartObject($cart),
  246. 'gtmProductsObjects' => $gtmProductsObject,
  247. ]);
  248. }
  249. /**
  250. * @Route("/add-coupon-ajax", name="fe_cart_widget_add_coupon_ajax", methods={"POST"})
  251. */
  252. public function addCouponAjax(
  253. Request $request,
  254. CouponService $couponService,
  255. CouponRepository $couponRepository
  256. ): Response
  257. {
  258. $code = $request->request->get('code');
  259. if (!Validate::not_null($code)) {
  260. return $this->json(["error" => true, 'message' => "Please enter the coupon code"]);
  261. }
  262. $cart = $this->cartService->getCart();
  263. if (!$cart instanceof Cart) {
  264. return $this->json(["error" => true, 'message' => "don't play with us"]);
  265. }
  266. $coupon = $couponRepository->findOneBy(array('code' => $code));
  267. if (!$coupon) {
  268. return $this->json([
  269. "error" => true,
  270. "message" => "Please add a valid Coupon",
  271. 'cart' => $this->getCartObject($cart),
  272. ]);
  273. }
  274. $validateCoupon = $couponService->validateCoupon($cart, $coupon, true);
  275. if ($validateCoupon !== true) {
  276. return $this->json([
  277. "error" => true,
  278. "message" => $validateCoupon,
  279. 'cart' => $this->getCartObject($cart),
  280. ]);
  281. }
  282. $cart->setCoupon($coupon);
  283. $this->em()->persist($cart);
  284. $this->em()->flush();
  285. $this->cartService->initCart($cart);
  286. return $this->json([
  287. "error" => false,
  288. "message" => $this->translator->trans("coupon_code_added_successfully_msg"),
  289. 'cart' => $this->getCartObject($cart),
  290. ]);
  291. }
  292. /**
  293. * @Route("/remove-coupon-ajax", name="fe_cart_widget_remove_coupon_ajax", methods={"POST"})
  294. */
  295. public function removeCouponAjax(): Response
  296. {
  297. $cart = $this->cartService->getCart();
  298. if (!$cart instanceof Cart) {
  299. return $this->json(["error" => true, 'message' => "don't play with us"]);
  300. }
  301. $cart->setCoupon(null);
  302. $this->em()->persist($cart);
  303. $this->em()->flush();
  304. $this->cartService->initCart($cart);
  305. return $this->json([
  306. "error" => false,
  307. "message" => $this->translator->trans("coupon_code_removed_successfully_msg"),
  308. 'cart' => $this->getCartObject($cart),
  309. ]);
  310. }
  311. private function doRemoveFromCartAndAddToWishlist(
  312. ProductFavoriteRepository $productFavoriteRepository,
  313. CartHasProductPrice $cartHasProductPrice = null
  314. ): void
  315. {
  316. $user = $this->getUser();
  317. if (!$cartHasProductPrice instanceof CartHasProductPrice or !$user instanceof User) {
  318. return;
  319. }
  320. $product = $cartHasProductPrice->getProductPrice()->getProduct();
  321. $productFavorite = $productFavoriteRepository->findOneBy([
  322. 'user' => $user,
  323. 'product' => $product,
  324. ]);
  325. if (!$productFavorite instanceof ProductFavorite) {
  326. $productFavorite = new ProductFavorite();
  327. $productFavorite->setProduct($product);
  328. $productFavorite->setUser($user);
  329. $this->em()->persist($productFavorite);
  330. }
  331. $this->em()->remove($cartHasProductPrice);
  332. $this->em()->flush();
  333. }
  334. public function getCartLastUpdateHash(Request $request): Response
  335. {
  336. $userAgent = $request->headers->get("User-Agent");
  337. foreach ($this->excludeAgents as $agent) {
  338. if (stripos($userAgent, $agent) !== false) {
  339. return new Response('null');
  340. }
  341. }
  342. $cart = $this->cartService->getCart(createCartIfNotExist: true);
  343. if (!$cart instanceof Cart) {
  344. return new Response('null');
  345. }
  346. $hash = $this->getLastUpdatedHash($cart);
  347. return new Response('"' . $hash . '"');
  348. }
  349. public function getLastUpdatedHash(Cart $cart): string
  350. {
  351. $lastCartUpdateDate = $this->cartRepository->getLastUpdateDate($cart);
  352. if ($lastCartUpdateDate instanceof \DateTimeInterface) {
  353. $lastCartUpdateDate = $lastCartUpdateDate->format(Date::DATE_FORMAT1);
  354. return md5($lastCartUpdateDate . "-" . $this->translator->getLocale() . "-" . $cart->getId());
  355. }
  356. return md5($this->translator->getLocale() . "-" . $cart->getId() . "-" . $cart->getCreated()->format(Date::DATE_FORMAT1));
  357. }
  358. private function getCartObject(Cart $cart): array
  359. {
  360. $object = $cart->getObj();
  361. $object["products"] = [];
  362. $object["lastUpdateHash"] = $this->getLastUpdatedHash($cart);
  363. $object['subTotal'] = round($cart->getSubTotal(), 2);
  364. $object['discount'] = round($cart->getDiscount(), 2);
  365. $object['shippingFee'] = round($cart->getShippingFees(), 2);
  366. $object['extraFees'] = round($cart->getExtraFees(), 2);
  367. $object['grandTotal'] = round($cart->getGrandTotal(), 2);
  368. foreach ($cart->getCartHasProductPrices() as $cartHasProductPrice) {
  369. $object['products'][] = $this->getCartHasProductPrice($cartHasProductPrice);
  370. }
  371. return $object;
  372. }
  373. private function getCartHasProductPrice(CartHasProductPrice $cartHasProductPrice): array
  374. {
  375. $shippingTime = $cartHasProductPrice->getShippingTime();
  376. $shippingTimeObj = null;
  377. if ($shippingTime instanceof ShippingTime) {
  378. $shippingTimeObj = [
  379. "id" => $shippingTime->getId(),
  380. "name" => $shippingTime->getName(),
  381. ];
  382. }
  383. return [
  384. "product" => $this->cartProductDTO->getProduct($cartHasProductPrice->getProductPrice()),
  385. "qty" => $cartHasProductPrice->getQty(),
  386. "shippingTime" => $shippingTimeObj
  387. ];
  388. }
  389. }