Failed Conditions
Push — 4.0 ( 31bfc5...86a776 )
by Kiyotaka
06:13
created

CartService::removeProduct()   A

Complexity

Conditions 5
Paths 7

Size

Total Lines 30

Duplication

Lines 9
Ratio 30 %

Code Coverage

Tests 20
CRAP Score 5

Importance

Changes 0
Metric Value
cc 5
nc 7
nop 1
dl 9
loc 30
ccs 20
cts 20
cp 1
crap 5
rs 9.1288
c 0
b 0
f 0
1
<?php
2
3
/*
4
 * This file is part of EC-CUBE
5
 *
6
 * Copyright(c) LOCKON CO.,LTD. All Rights Reserved.
7
 *
8
 * http://www.lockon.co.jp/
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Eccube\Service;
15
16
use Doctrine\ORM\EntityManagerInterface;
17
use Doctrine\ORM\UnitOfWork;
18
use Eccube\Entity\Cart;
19
use Eccube\Entity\CartItem;
20
use Eccube\Entity\Customer;
21
use Eccube\Entity\ItemHolderInterface;
22
use Eccube\Entity\ProductClass;
23
use Eccube\Repository\CartRepository;
24
use Eccube\Repository\OrderRepository;
25
use Eccube\Repository\ProductClassRepository;
26
use Eccube\Service\Cart\CartItemAllocator;
27
use Eccube\Service\Cart\CartItemComparator;
28
use Eccube\Util\StringUtil;
29
use Symfony\Component\HttpFoundation\Session\SessionInterface;
30
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
31
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
32
33
class CartService
34
{
35
    /**
36
     * @var Cart[]
37
     */
38
    protected $carts;
39
40
    /**
41
     * @var SessionInterface
42
     */
43
    protected $session;
44
45
    /**
46
     * @var \Doctrine\ORM\EntityManagerInterface
47
     */
48
    protected $entityManager;
49
50
    /**
51
     * @var ItemHolderInterface
52
     *
53
     * @deprecated
54
     */
55
    protected $cart;
56
57
    /**
58
     * @var ProductClassRepository
59
     */
60
    protected $productClassRepository;
61
62
    /**
63
     * @var CartRepository
64
     */
65
    protected $cartRepository;
66
67
    /**
68
     * @var CartItemComparator
69
     */
70
    protected $cartItemComparator;
71
72
    /**
73
     * @var CartItemAllocator
74
     */
75
    protected $cartItemAllocator;
76
77
    /**
78
     * @var OrderHelper
79
     */
80
    protected $orderHelper;
81
82
    /**
83
     * @var OrderRepository
84
     */
85
    protected $orderRepository;
86
87
    /**
88
     * @var TokenStorageInterface
89
     */
90
    protected $tokenStorage;
91
92
    /**
93
     * @var AuthorizationCheckerInterface
94
     */
95
    protected $authorizationChecker;
96
97
    /**
98
     * CartService constructor.
99
     *
100
     * @param SessionInterface $session
101
     * @param EntityManagerInterface $entityManager
102
     * @param ProductClassRepository $productClassRepository
103
     * @param CartItemComparator $cartItemComparator
104
     * @param CartItemAllocator $cartItemAllocator
105
     * @param OrderHelper $orderHelper
106
     * @param TokenStorageInterface $tokenStorage
107
     * @param AuthorizationCheckerInterface $authorizationChecker
108
     */
109 173
    public function __construct(
110
        SessionInterface $session,
111
        EntityManagerInterface $entityManager,
112
        ProductClassRepository $productClassRepository,
113
        CartRepository $cartRepository,
114
        CartItemComparator $cartItemComparator,
115
        CartItemAllocator $cartItemAllocator,
116
        OrderHelper $orderHelper,
117
        OrderRepository $orderRepository,
118
        TokenStorageInterface $tokenStorage,
119
        AuthorizationCheckerInterface $authorizationChecker
120
    ) {
121 173
        $this->session = $session;
122 173
        $this->entityManager = $entityManager;
123 173
        $this->productClassRepository = $productClassRepository;
124 173
        $this->cartRepository = $cartRepository;
125 173
        $this->cartItemComparator = $cartItemComparator;
126 173
        $this->cartItemAllocator = $cartItemAllocator;
127 173
        $this->orderHelper = $orderHelper;
128 173
        $this->orderRepository = $orderRepository;
129 173
        $this->tokenStorage = $tokenStorage;
130 173
        $this->authorizationChecker = $authorizationChecker;
131
    }
132
133 132
    public function getCarts()
134
    {
135 132
        if (!empty($this->carts)) {
136 86
            return $this->carts;
137
        }
138
139 132
        if ($this->getUser()) {
140 65
            $this->carts = $this->getPersistedCarts();
141
        } else {
142 67
            $this->carts = $this->getSessionCarts();
143
        }
144
145 132
        return $this->carts;
146
    }
147
148
    /**
149
     * 永続化されたカートを返す
150
     *
151
     * @return Cart[]
152
     */
153 65
    public function getPersistedCarts()
154
    {
155 65
        return $this->cartRepository->findBy(['Customer' => $this->getUser()]);
156
    }
157
158
    /**
159
     * セッションにあるカートを返す
160
     *
161
     * @return Cart[]
162
     */
163 67
    public function getSessionCarts()
164
    {
165 67
        $cartKeys = $this->session->get('cart_keys', []);
166
167 67
        return $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'DESC']);
168
    }
169
170
    /**
171
     * 会員が保持する永続化されたカートと、非会員時のカートをマージする.
172
     */
173
    public function mergeFromPersistedCart()
174
    {
175
        $CartItems = [];
176
        foreach ($this->getPersistedCarts() as $Cart) {
177
            $CartItems = $this->mergeCartItems($Cart->getCartItems(), $CartItems);
178
        }
179
180
        // セッションにある非会員カートとDBから取得した会員カートをマージする.
181
        foreach ($this->getSessionCarts() as $Cart) {
182
            $CartItems = $this->mergeCartItems($Cart->getCartItems(), $CartItems);
183
        }
184
185
        $this->restoreCarts($CartItems);
186
    }
187
188
    /**
189
     * @return ItemHolderInterface|Cart
190
     */
191 65
    public function getCart()
192
    {
193 65
        $Carts = $this->getCarts();
194
195 65
        if (empty($Carts)) {
196 4
            return null;
197
        }
198
199 63
        $cartKeys = $this->session->get('cart_keys', []);
200
        $Cart = null;
201
        if (count($cartKeys) > 0) {
202
            foreach ($Carts as $cart) {
203
                if ($cart->getCartKey() === current($cartKeys)) {
204
                    $Cart = $cart;
205
                    break;
206
                }
207 87
            } 
208
        } else {
209
            $Cart = current($Carts);
210 87
        }
211
212 87
        return $Cart;
213 39
    }
214
215
    /**
216 87
     * @param CartItem[] $cartItems
217
     *
218
     * @return CartItem[]
219
     */
220
    protected function mergeAllCartItems($cartItems = [])
221
    {
222
        /** @var CartItem[] $allCartItems */
223
        $allCartItems = [];
224
225 87
        foreach ($this->getCarts() as $Cart) {
226
            $allCartItems = $this->mergeCartItems($Cart->getCartItems(), $allCartItems);
227 87
        }
228 86
229 86
        return $this->mergeCartItems($cartItems, $allCartItems);
230
    }
231 38
232 36
    /**
233 36
     * @param $cartItems
234 38
     * @param $allCartItems
235
     *
236
     * @return array
237 86
     */
238 86
    protected function mergeCartItems($cartItems, $allCartItems)
239
    {
240
        foreach ($cartItems as $item) {
241
            $itemExists = false;
242 87
            foreach ($allCartItems as $itemInArray) {
243
                // 同じ明細があればマージする
244
                if ($this->cartItemComparator->compare($item, $itemInArray)) {
245 87
                    $itemInArray->setQuantity($itemInArray->getQuantity() + $item->getQuantity());
246
                    $itemExists = true;
247 87
                    break;
248 2
                }
249 1
            }
250 1
            if (!$itemExists) {
251 1
                $allCartItems[] = $item;
252
            }
253 1
        }
254 1
255
        return $allCartItems;
256 2
    }
257
258
    protected function restoreCarts($cartItems)
259
    {
260 87
        foreach ($this->getCarts() as $Cart) {
261
            foreach ($Cart->getCartItems() as $i) {
262 87
                $this->entityManager->remove($i);
263 86
                $this->entityManager->flush($i);
0 ignored issues
show
Unused Code introduced by
The call to EntityManagerInterface::flush() has too many arguments starting with $i.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
264 86
            }
265
            $this->entityManager->remove($Cart);
266 86
            $this->entityManager->flush($Cart);
0 ignored issues
show
Unused Code introduced by
The call to EntityManagerInterface::flush() has too many arguments starting with $Cart.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
267 16
        }
268 16
        $this->carts = [];
269 16
270
        /** @var Cart[] $Carts */
271
        $Carts = [];
272 86
273 86
        foreach ($cartItems as $item) {
274 34
            $allocatedId = $this->cartItemAllocator->allocate($item);
275 34
            $cartKey = $this->createCartKey($allocatedId, $this->getUser());
276 34
277
            if (isset($Carts[$cartKey])) {
278 34
                $Cart = $Carts[$cartKey];
279 34
                $Cart->addCartItem($item);
280
                $item->setCart($Cart);
281 86
            } else {
282 86
                /** @var Cart $Cart */
283 86
                $Cart = $this->cartRepository->findOneBy(['cart_key' => $cartKey]);
284 86
                if ($Cart) {
285 86
                    foreach ($Cart->getCartItems() as $i) {
286
                        $this->entityManager->remove($i);
287
                        $this->entityManager->flush($i);
0 ignored issues
show
Unused Code introduced by
The call to EntityManagerInterface::flush() has too many arguments starting with $i.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
288
                    }
289 87
                    $this->entityManager->remove($Cart);
290
                    $this->entityManager->flush($Cart);
0 ignored issues
show
Unused Code introduced by
The call to EntityManagerInterface::flush() has too many arguments starting with $Cart.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
291
                }
292
                $Cart = new Cart();
293
                $Cart->setCartKey($cartKey);
294
                $Cart->addCartItem($item);
295
                $item->setCart($Cart);
296
                $Carts[$cartKey] = $Cart;
297
            }
298
        }
299
300 86
        $this->carts = array_values($Carts);
301
    }
302 86
303 45
    /**
304 45
     * カートに商品を追加します.
305 45
     *
306 45
     * @param $ProductClass ProductClass 商品規格
307 45
     * @param $quantity int 数量
308
     *
309
     * @return bool 商品を追加できた場合はtrue
310
     */
311
    public function addProduct($ProductClass, $quantity = 1)
312 86
    {
313 86 View Code Duplication
        if (!$ProductClass instanceof ProductClass) {
314
            $ProductClassId = $ProductClass;
315
            $ProductClass = $this->entityManager
316 86
                ->getRepository(ProductClass::class)
317 86
                ->find($ProductClassId);
318
            if (is_null($ProductClass)) {
319
                return false;
320
            }
321 86
        }
322 86
323 86
        $ClassCategory1 = $ProductClass->getClassCategory1();
324 86
        if ($ClassCategory1 && !$ClassCategory1->isVisible()) {
325
            return false;
326 86
        }
327 86
        $ClassCategory2 = $ProductClass->getClassCategory2();
328
        if ($ClassCategory2 && !$ClassCategory2->isVisible()) {
329 86
            return false;
330
        }
331
332 2
        $newItem = new CartItem();
333
        $newItem->setQuantity($quantity);
334 2
        $newItem->setPrice($ProductClass->getPrice02IncTax());
335 1
        $newItem->setProductClass($ProductClass);
336 1
337 1
        $allCartItems = $this->mergeAllCartItems([$newItem]);
338 1
        $this->restoreCarts($allCartItems);
339 1
340
        return true;
341
    }
342
343
    public function removeProduct($ProductClass)
344 2
    {
345 2 View Code Duplication
        if (!$ProductClass instanceof ProductClass) {
346 2
            $ProductClassId = $ProductClass;
347
            $ProductClass = $this->entityManager
348 2
                ->getRepository(ProductClass::class)
349 2
                ->find($ProductClassId);
350 2
            if (is_null($ProductClass)) {
351 1
                return false;
352 1
            }
353 1
        }
354
355
        $removeItem = new CartItem();
356
        $removeItem->setPrice($ProductClass->getPrice02IncTax());
357 2
        $removeItem->setProductClass($ProductClass);
358 2
359
        $allCartItems = $this->mergeAllCartItems();
360 2
        $foundIndex = -1;
361
        foreach ($allCartItems as $index => $itemInCart) {
362
            if ($this->cartItemComparator->compare($itemInCart, $removeItem)) {
363 80
                $foundIndex = $index;
364
                break;
365 80
            }
366 80
        }
367 78
368 78
        array_splice($allCartItems, $foundIndex, 1);
369 78
        $this->restoreCarts($allCartItems);
370 78
371 78
        return true;
372
    }
373 78
374 78
    public function save()
375
    {
376
        $cartKeys = [];
377 80
        foreach ($this->carts as $Cart) {
378
            $Cart->setCustomer($this->getUser());
379 80
            $this->entityManager->persist($Cart);
380
            foreach ($Cart->getCartItems() as $item) {
381
                $this->entityManager->persist($item);
382
                $this->entityManager->flush($item);
0 ignored issues
show
Unused Code introduced by
The call to EntityManagerInterface::flush() has too many arguments starting with $item.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
383
            }
384
            $this->entityManager->flush($Cart);
0 ignored issues
show
Unused Code introduced by
The call to EntityManagerInterface::flush() has too many arguments starting with $Cart.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
385
            $cartKeys[] = $Cart->getCartKey();
386
        }
387 52
388
        $this->session->set('cart_keys', $cartKeys);
389 52
390
        return;
391 52
    }
392
393
    /**
394
     * @param  string $pre_order_id
395
     *
396
     * @return \Eccube\Service\CartService
397 51
     */
398
    public function setPreOrderId($pre_order_id)
399 51
    {
400
        $this->getCart()->setPreOrderId($pre_order_id);
401
402
        return $this;
403
    }
404
405 10
    /**
406
     * @return string
407 10
     */
408 10
    public function getPreOrderId()
409 8
    {
410 8
        return $this->getCart()->getPreOrderId();
411 7
    }
412 7
413
    /**
414 7
     * @return \Eccube\Service\CartService
415 7
     */
416
    public function clear()
417
    {
418 7
        $Carts = $this->getCarts();
419 7
        if (!empty($Carts)) {
420
            $removed = $this->getCart();
421
            if ($removed && UnitOfWork::STATE_MANAGED === $this->entityManager->getUnitOfWork()->getEntityState($removed)) {
422
                $this->entityManager->remove($removed);
423 10
                $this->entityManager->flush($removed);
0 ignored issues
show
Unused Code introduced by
The call to EntityManagerInterface::flush() has too many arguments starting with $removed.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
424
425
                $cartKeys = [];
426
                foreach ($Carts as $key => $Cart) {
427
                    // テーブルから削除されたカートを除外する
428
                    if ($Cart == $removed) {
429 1
                        unset($Carts[$key]);
430
                    }
431 1
                    $cartKeys[] = $Cart->getCartKey();
432
                }
433
                $this->session->set('cart_keys', $cartKeys);
434
                // 注文完了のカートキーをセッションから削除する
435
                $this->session->remove('cart_key');
436
                $this->carts = $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'DESC']);
437
            }
438
        }
439 53
440
        return $this;
441 53
    }
442 53
443 53
    /**
444 53
     * @param CartItemComparator $cartItemComparator
445 53
     */
446 53
    public function setCartItemComparator($cartItemComparator)
447 53
    {
448
        $this->cartItemComparator = $cartItemComparator;
449
    }
450 132
451
    /**
452 132
     * カートキーで指定したインデックスにあるカートを優先にする
453 10
     *
454
     * @param string $cartKey カートキー
455
     */
456 122
    public function setPrimary($cartKey)
457
    {
458 57
        $Carts = $this->getCarts();
459
        $primary = $Carts[0];
460
        $index = 0;
461 65
        foreach ($Carts as $key => $Cart) {
462
            if ($Cart->getCartKey() === $cartKey) {
463
                $index = $key;
464
                $primary = $Carts[$index];
465
                break;
466
            }
467 86
        }
468
        $prev = $Carts[0];
469 86
        array_splice($Carts, 0, 1, [$primary]);
470 51
        array_splice($Carts, $index, 1, [$prev]);
471
        $this->carts = $Carts;
472
        $this->save();
473 35
    }
474 17
475
    protected function getUser()
476
    {
477
        if (null === $token = $this->tokenStorage->getToken()) {
478 35
            return;
479 35
        }
480 35
481 35
        if (!is_object($user = $token->getUser())) {
482
            // e.g. anonymous authentication
483 35
            return;
484
        }
485 35
486
        return $user;
487
    }
488
489
    /**
490
     * @param string $allocatedId
491
     */
492
    protected function createCartKey($allocatedId, Customer $Customer = null)
493
    {
494
        if ($Customer instanceof Customer) {
495
            return $Customer->getId().'_'.$allocatedId;
496
        }
497
498
        if ($this->session->has('cart_key_prefix')) {
499
            return $this->session->get('cart_key_prefix').'_'.$allocatedId;
500
        }
501
502
        do {
503
            $random = StringUtil::random(32);
504
            $cartKey = $random.'_'.$allocatedId;
505
            $Cart = $this->cartRepository->findOneBy(['cart_key' => $cartKey]);
506
        } while ($Cart);
507
508
        $this->session->set('cart_key_prefix', $random);
509
510
        return $cartKey;
511
    }
512
}
513