vendor/shopware/storefront/Controller/CartLineItemController.php line 239

Open in your IDE?
  1. <?php declare(strict_types=1);
  2. namespace Shopware\Storefront\Controller;
  3. use Shopware\Core\Checkout\Cart\Cart;
  4. use Shopware\Core\Checkout\Cart\CartException;
  5. use Shopware\Core\Checkout\Cart\Error\Error;
  6. use Shopware\Core\Checkout\Cart\Exception\LineItemNotFoundException;
  7. use Shopware\Core\Checkout\Cart\LineItem\LineItem;
  8. use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
  9. use Shopware\Core\Checkout\Promotion\Cart\PromotionCartAddedInformationError;
  10. use Shopware\Core\Checkout\Promotion\Cart\PromotionItemBuilder;
  11. use Shopware\Core\Content\Product\Cart\ProductLineItemFactory;
  12. use Shopware\Core\Content\Product\Exception\ProductNotFoundException;
  13. use Shopware\Core\Content\Product\SalesChannel\AbstractProductListRoute;
  14. use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
  15. use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
  16. use Shopware\Core\Framework\Feature;
  17. use Shopware\Core\Framework\Log\Package;
  18. use Shopware\Core\Framework\Routing\Annotation\RouteScope;
  19. use Shopware\Core\Framework\Routing\Annotation\Since;
  20. use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
  21. use Shopware\Core\Framework\Util\HtmlSanitizer;
  22. use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
  23. use Shopware\Core\Profiling\Profiler;
  24. use Shopware\Core\System\SalesChannel\SalesChannelContext;
  25. use Symfony\Component\HttpFoundation\Request;
  26. use Symfony\Component\HttpFoundation\Response;
  27. use Symfony\Component\Routing\Annotation\Route;
  28. /**
  29.  * @Route(defaults={"_routeScope"={"storefront"}})
  30.  *
  31.  * @deprecated tag:v6.5.0 - reason:becomes-internal - Will be internal
  32.  */
  33. #[Package('storefront')]
  34. class CartLineItemController extends StorefrontController
  35. {
  36.     private CartService $cartService;
  37.     private PromotionItemBuilder $promotionItemBuilder;
  38.     private ProductLineItemFactory $productLineItemFactory;
  39.     private HtmlSanitizer $htmlSanitizer;
  40.     private AbstractProductListRoute $productListRoute;
  41.     /**
  42.      * @internal
  43.      */
  44.     public function __construct(
  45.         CartService $cartService,
  46.         PromotionItemBuilder $promotionItemBuilder,
  47.         ProductLineItemFactory $productLineItemFactory,
  48.         HtmlSanitizer $htmlSanitizer,
  49.         AbstractProductListRoute $productListRoute
  50.     ) {
  51.         $this->cartService $cartService;
  52.         $this->promotionItemBuilder $promotionItemBuilder;
  53.         $this->productLineItemFactory $productLineItemFactory;
  54.         $this->htmlSanitizer $htmlSanitizer;
  55.         $this->productListRoute $productListRoute;
  56.     }
  57.     /**
  58.      * @Since("6.0.0.0")
  59.      * @Route("/checkout/line-item/delete/{id}", name="frontend.checkout.line-item.delete", methods={"POST", "DELETE"}, defaults={"XmlHttpRequest": true})
  60.      */
  61.     public function deleteLineItem(Cart $cartstring $idRequest $requestSalesChannelContext $context): Response
  62.     {
  63.         return Profiler::trace('cart::delete-line-item', function () use ($cart$id$request$context) {
  64.             try {
  65.                 if (!$cart->has($id)) {
  66.                     if (Feature::isActive('v6.5.0.0')) {
  67.                         throw CartException::lineItemNotFound($id);
  68.                     }
  69.                     throw new LineItemNotFoundException($id);
  70.                 }
  71.                 $cart $this->cartService->remove($cart$id$context);
  72.                 if (!$this->traceErrors($cart)) {
  73.                     $this->addFlash(self::SUCCESS$this->trans('checkout.cartUpdateSuccess'));
  74.                 }
  75.             } catch (\Exception $exception) {
  76.                 $this->addFlash(self::DANGER$this->trans('error.message-default'));
  77.             }
  78.             return $this->createActionResponse($request);
  79.         });
  80.     }
  81.     /**
  82.      * @Since("6.0.0.0")
  83.      * This is the storefront controller action for adding a promotion.
  84.      * It has some individual code for the storefront layouts, like visual
  85.      * error and success messages.
  86.      *
  87.      * @Route("/checkout/promotion/add", name="frontend.checkout.promotion.add", defaults={"XmlHttpRequest": true}, methods={"POST"})
  88.      */
  89.     public function addPromotion(Cart $cartRequest $requestSalesChannelContext $context): Response
  90.     {
  91.         return Profiler::trace('cart::add-promotion', function () use ($cart$request$context) {
  92.             try {
  93.                 $code = (string) $request->request->get('code');
  94.                 if ($code === '') {
  95.                     throw new \InvalidArgumentException('Code is required');
  96.                 }
  97.                 $lineItem $this->promotionItemBuilder->buildPlaceholderItem($code);
  98.                 $cart $this->cartService->add($cart$lineItem$context);
  99.                 // we basically show all cart errors or notices
  100.                 // at the moments its not possible to show success messages with "green" color
  101.                 // from the cart...thus it has to be done in the storefront level
  102.                 // so if we have an promotion added notice, we simply convert this to
  103.                 // a success flash message
  104.                 $addedEvents $cart->getErrors()->filterInstance(PromotionCartAddedInformationError::class);
  105.                 if ($addedEvents->count() > 0) {
  106.                     $this->addFlash(self::SUCCESS$this->trans('checkout.codeAddedSuccessful'));
  107.                     return $this->createActionResponse($request);
  108.                 }
  109.                 // if we have no custom error message above
  110.                 // then simply continue with the default display
  111.                 // of the cart errors and notices
  112.                 $this->traceErrors($cart);
  113.             } catch (\Exception $exception) {
  114.                 $this->addFlash(self::DANGER$this->trans('error.message-default'));
  115.             }
  116.             return $this->createActionResponse($request);
  117.         });
  118.     }
  119.     /**
  120.      * @Since("6.0.0.0")
  121.      * @Route("/checkout/line-item/change-quantity/{id}", name="frontend.checkout.line-item.change-quantity", defaults={"XmlHttpRequest": true}, methods={"POST"})
  122.      */
  123.     public function changeQuantity(Cart $cartstring $idRequest $requestSalesChannelContext $context): Response
  124.     {
  125.         return Profiler::trace('cart::change-quantity', function () use ($cart$id$request$context) {
  126.             try {
  127.                 $quantity $request->get('quantity');
  128.                 if ($quantity === null) {
  129.                     throw new \InvalidArgumentException('quantity field is required');
  130.                 }
  131.                 if (!$cart->has($id)) {
  132.                     if (Feature::isActive('v6.5.0.0')) {
  133.                         throw CartException::lineItemNotFound($id);
  134.                     }
  135.                     throw new LineItemNotFoundException($id);
  136.                 }
  137.                 $cart $this->cartService->changeQuantity($cart$id, (int) $quantity$context);
  138.                 if (!$this->traceErrors($cart)) {
  139.                     $this->addFlash(self::SUCCESS$this->trans('checkout.cartUpdateSuccess'));
  140.                 }
  141.             } catch (\Exception $exception) {
  142.                 $this->addFlash(self::DANGER$this->trans('error.message-default'));
  143.             }
  144.             return $this->createActionResponse($request);
  145.         });
  146.     }
  147.     /**
  148.      * @Since("6.0.0.0")
  149.      * @Route("/checkout/product/add-by-number", name="frontend.checkout.product.add-by-number", methods={"POST"})
  150.      */
  151.     public function addProductByNumber(Request $requestSalesChannelContext $context): Response
  152.     {
  153.         return Profiler::trace('cart::add-product-by-number', function () use ($request$context) {
  154.             $number = (string) $request->request->get('number');
  155.             if (!$number) {
  156.                 throw new MissingRequestParameterException('number');
  157.             }
  158.             $criteria = new Criteria();
  159.             $criteria->setLimit(1);
  160.             $criteria->addFilter(new EqualsFilter('productNumber'$number));
  161.             $data $this->productListRoute->load($criteria$context)->getProducts()->getIds();
  162.             if (empty($data)) {
  163.                 $this->addFlash(self::DANGER$this->trans(
  164.                     'error.productNotFound',
  165.                     ['%number%' => $this->htmlSanitizer->sanitize($numbernulltrue)]
  166.                 ));
  167.                 return $this->createActionResponse($request);
  168.             }
  169.             /** @var string $productId */
  170.             $productId array_shift($data);
  171.             $product $this->productLineItemFactory->create($productId);
  172.             $cart $this->cartService->getCart($context->getToken(), $context);
  173.             $cart $this->cartService->add($cart$product$context);
  174.             if (!$this->traceErrors($cart)) {
  175.                 $this->addFlash(self::SUCCESS$this->trans('checkout.addToCartSuccess', ['%count%' => 1]));
  176.             }
  177.             return $this->createActionResponse($request);
  178.         });
  179.     }
  180.     /**
  181.      * @Since("6.0.0.0")
  182.      * @Route("/checkout/line-item/add", name="frontend.checkout.line-item.add", methods={"POST"}, defaults={"XmlHttpRequest"=true})
  183.      *
  184.      * requires the provided items in the following form
  185.      * 'lineItems' => [
  186.      *     'anyKey' => [
  187.      *         'id' => 'someKey'
  188.      *         'quantity' => 2,
  189.      *         'type' => 'someType'
  190.      *     ],
  191.      *     'randomKey' => [
  192.      *         'id' => 'otherKey'
  193.      *         'quantity' => 2,
  194.      *         'type' => 'otherType'
  195.      *     ]
  196.      * ]
  197.      */
  198.     public function addLineItems(Cart $cartRequestDataBag $requestDataBagRequest $requestSalesChannelContext $context): Response
  199.     {
  200.         return Profiler::trace('cart::add-line-item', function () use ($cart$requestDataBag$request$context) {
  201.             /** @var RequestDataBag|null $lineItems */
  202.             $lineItems $requestDataBag->get('lineItems');
  203.             if (!$lineItems) {
  204.                 throw new MissingRequestParameterException('lineItems');
  205.             }
  206.             $count 0;
  207.             try {
  208.                 $items = [];
  209.                 /** @var RequestDataBag $lineItemData */
  210.                 foreach ($lineItems as $lineItemData) {
  211.                     $lineItem = new LineItem(
  212.                         $lineItemData->getAlnum('id'),
  213.                         $lineItemData->getAlnum('type'),
  214.                         $lineItemData->get('referencedId'),
  215.                         $lineItemData->getInt('quantity'1)
  216.                     );
  217.                     $lineItem->setStackable($lineItemData->getBoolean('stackable'true));
  218.                     $lineItem->setRemovable($lineItemData->getBoolean('removable'true));
  219.                     $count += $lineItem->getQuantity();
  220.                     $items[] = $lineItem;
  221.                 }
  222.                 $cart $this->cartService->add($cart$items$context);
  223.                 if (!$this->traceErrors($cart)) {
  224.                     $this->addFlash(self::SUCCESS$this->trans('checkout.addToCartSuccess', ['%count%' => $count]));
  225.                 }
  226.             } catch (ProductNotFoundException $exception) {
  227.                 $this->addFlash(self::DANGER$this->trans('error.addToCartError'));
  228.             }
  229.             return $this->createActionResponse($request);
  230.         });
  231.     }
  232.     private function traceErrors(Cart $cart): bool
  233.     {
  234.         if ($cart->getErrors()->count() <= 0) {
  235.             return false;
  236.         }
  237.         $this->addCartErrors($cart, function (Error $error) {
  238.             return $error->isPersistent();
  239.         });
  240.         return true;
  241.     }
  242. }