Passed
Push — master ( 0dcd20...e33123 )
by Christian
10:55 queued 10s
created

CartLineItemController::addCartErrorsToFlashBag()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 11
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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