Completed
Pull Request — experimental/sf (#3460)
by Kiyotaka
63:10
created

CartService::getCart()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
ccs 1
cts 1
cp 1
crap 2
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 132
            $this->carts = $this->getPersistedCarts();
141
        } else {
142 132
            $this->carts = $this->getSessionCarts();
143
        }
144
145
        return $this->carts;
146
    }
147
148
    /**
149
     * 永続化されたカートを返す
150
     *
151
     * @return Cart[]
152
     */
153
    public function getPersistedCarts()
154
    {
155
        return $this->cartRepository->findBy(['Customer' => $this->getUser()]);
156
    }
157
158
    /**
159
     * セッションにあるカートを返す
160
     *
161
     * @return Cart[]
162
     */
163
    public function getSessionCarts()
164
    {
165
        $cartKeys = $this->session->get('cart_keys', []);
166
167 65
        return $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'DESC']);
168
    }
169 65
170
    /**
171 65
     * 会員が保持する永続化されたカートと、非会員時のカートをマージする.
172 4
     *
173
     * @param Customer $Customer
174
     */
175 63
    public function mergeFromPersistedCart(Customer $Customer)
0 ignored issues
show
Unused Code introduced by
The parameter $Customer is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
176
    {
177
        $CartItems = [];
178
        foreach ($this->getPersistedCarts() as $Cart) {
179
            $CartItems = $this->mergeCartItems($Cart->getCartItems(), $CartItems);
180
        }
181
182
        // セッションにある非会員カートとDBから取得した会員カートをマージする.
183 87
        foreach ($this->getSessionCarts() as $Cart) {
184
            $CartItems = $this->mergeCartItems($Cart->getCartItems(), $CartItems);
185
        }
186 87
187
        $this->restoreCarts($CartItems);
188 87
    }
189 39
190
    /**
191
     * @return ItemHolderInterface|Cart
192 87
     */
193
    public function getCart()
194
    {
195
        $Carts = $this->getCarts();
196
197
        if (empty($Carts)) {
198
            return null;
199
        }
200
201 87
        return current($Carts);
202
    }
203 87
204 86
    /**
205 86
     * @param CartItem[] $cartItems
206
     *
207 38
     * @return CartItem[]
208 36
     */
209 36
    protected function mergeAllCartItems($cartItems = [])
210 38
    {
211
        /** @var CartItem[] $allCartItems */
212
        $allCartItems = [];
213 86
214 86
        foreach ($this->getCarts() as $Cart) {
215
            $allCartItems = $this->mergeCartItems($Cart->getCartItems(), $allCartItems);
216
        }
217
218 87
        return $this->mergeCartItems($cartItems, $allCartItems);
219
    }
220
221 87
    /**
222
     * @param $cartItems
223 87
     * @param $allCartItems
224 2
     *
225 1
     * @return array
226 1
     */
227 1
    protected function mergeCartItems($cartItems, $allCartItems)
228
    {
229 1
        foreach ($cartItems as $item) {
230 1
            $itemExists = false;
231
            foreach ($allCartItems as $itemInArray) {
232 2
                // 同じ明細があればマージする
233
                if ($this->cartItemComparator->compare($item, $itemInArray)) {
234
                    $itemInArray->setQuantity($itemInArray->getQuantity() + $item->getQuantity());
235
                    $itemExists = true;
236 87
                    break;
237
                }
238 87
            }
239 86
            if (!$itemExists) {
240 86
                $allCartItems[] = $item;
241
            }
242 86
        }
243 16
244 16
        return $allCartItems;
245 16
    }
246
247
    protected function restoreCarts($cartItems)
248 86
    {
249 86
        if (empty($cartItems)) {
250 34
            foreach ($this->getCarts() as $Cart) {
251 34
                foreach ($Cart->getCartItems() as $i) {
252 34
                    $this->entityManager->remove($i);
253
                    $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...
254 34
                }
255 34
                $this->entityManager->remove($Cart);
256
                $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...
257 86
            }
258 86
            $this->carts = [];
259 86
        }
260 86
261 86
        /** @var Cart[] $Carts */
262
        $Carts = [];
263
264
        foreach ($cartItems as $item) {
265 87
            $allocatedId = $this->cartItemAllocator->allocate($item);
266
            $cartKey = $this->createCartKey($allocatedId, $this->getUser());
267
268
            if (isset($Carts[$cartKey])) {
269
                $Cart = $Carts[$cartKey];
270
                $Cart->addCartItem($item);
271
                $item->setCart($Cart);
272
            } else {
273
                /** @var Cart $Cart */
274
                $Cart = $this->cartRepository->findOneBy(['cart_key' => $cartKey]);
275
                if ($Cart) {
276 86
                    foreach ($Cart->getCartItems() as $i) {
277
                        $this->entityManager->remove($i);
278 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...
279 45
                    }
280 45
                    $this->entityManager->remove($Cart);
281 45
                    $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...
282 45
                }
283 45
                $Cart = new Cart();
284
                $Cart->setCartKey($cartKey);
285
                $Cart->addCartItem($item);
286
                $item->setCart($Cart);
287
                $Carts[$cartKey] = $Cart;
288 86
            }
289 86
        }
290
291
        $this->carts = array_values($Carts);
292 86
    }
293 86
294
    /**
295
     * カートに商品を追加します.
296
     *
297 86
     * @param $ProductClass ProductClass 商品規格
298 86
     * @param $quantity int 数量
299 86
     *
300 86
     * @return bool 商品を追加できた場合はtrue
301
     */
302 86
    public function addProduct($ProductClass, $quantity = 1)
303 86
    {
304 View Code Duplication
        if (!$ProductClass instanceof ProductClass) {
305 86
            $ProductClassId = $ProductClass;
306
            $ProductClass = $this->entityManager
307
                ->getRepository(ProductClass::class)
308 2
                ->find($ProductClassId);
309
            if (is_null($ProductClass)) {
310 2
                return false;
311 1
            }
312 1
        }
313 1
314 1
        $ClassCategory1 = $ProductClass->getClassCategory1();
315 1
        if ($ClassCategory1 && !$ClassCategory1->isVisible()) {
316
            return false;
317
        }
318
        $ClassCategory2 = $ProductClass->getClassCategory2();
319
        if ($ClassCategory2 && !$ClassCategory2->isVisible()) {
320 2
            return false;
321 2
        }
322 2
323
        $newItem = new CartItem();
324 2
        $newItem->setQuantity($quantity);
325 2
        $newItem->setPrice($ProductClass->getPrice02IncTax());
326 2
        $newItem->setProductClass($ProductClass);
327 1
328 1
        $allCartItems = $this->mergeAllCartItems([$newItem]);
329 1
        $this->restoreCarts($allCartItems);
330
331
        return true;
332
    }
333 2
334 2
    public function removeProduct($ProductClass)
335
    {
336 2 View Code Duplication
        if (!$ProductClass instanceof ProductClass) {
337
            $ProductClassId = $ProductClass;
338
            $ProductClass = $this->entityManager
339 80
                ->getRepository(ProductClass::class)
340
                ->find($ProductClassId);
341 80
            if (is_null($ProductClass)) {
342 80
                return false;
343 78
            }
344 78
        }
345 78
346 78
        $removeItem = new CartItem();
347 78
        $removeItem->setPrice($ProductClass->getPrice02IncTax());
348
        $removeItem->setProductClass($ProductClass);
349 78
350 78
        $allCartItems = $this->mergeAllCartItems();
351
        $foundIndex = -1;
352
        foreach ($allCartItems as $index => $itemInCart) {
353 80
            if ($this->cartItemComparator->compare($itemInCart, $removeItem)) {
354
                $foundIndex = $index;
355 80
                break;
356
            }
357
        }
358
359
        array_splice($allCartItems, $foundIndex, 1);
360
        $this->restoreCarts($allCartItems);
361
362
        return true;
363 52
    }
364
365 52
    public function save()
366
    {
367 52
        $cartKeys = [];
368
        foreach ($this->carts as $Cart) {
369
            $Cart->setCustomer($this->getUser());
370
            $this->entityManager->persist($Cart);
371
            foreach ($Cart->getCartItems() as $item) {
372
                $this->entityManager->persist($item);
373 51
                $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...
374
            }
375 51
            $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...
376
            $cartKeys[] = $Cart->getCartKey();
377
        }
378
379
        $this->session->set('cart_keys', $cartKeys);
380
381 10
        return;
382
    }
383 10
384 10
    /**
385 8
     * @param  string $pre_order_id
386 8
     *
387 7
     * @return \Eccube\Service\CartService
388 7
     */
389
    public function setPreOrderId($pre_order_id)
390 7
    {
391 7
        $this->getCart()->setPreOrderId($pre_order_id);
392
393
        return $this;
394 7
    }
395 7
396
    /**
397
     * @return string
398
     */
399 10
    public function getPreOrderId()
400
    {
401
        return $this->getCart()->getPreOrderId();
402
    }
403
404
    /**
405 1
     * @return \Eccube\Service\CartService
406
     */
407 1
    public function clear()
408
    {
409
        $Carts = $this->getCarts();
410
        if (!empty($Carts)) {
411
            $removed = array_shift($Carts);
412
            if ($removed && UnitOfWork::STATE_MANAGED === $this->entityManager->getUnitOfWork()->getEntityState($removed)) {
413
                $this->entityManager->remove($removed);
414
                $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...
415 53
416
                $cartKeys = [];
417 53
                foreach ($Carts as $Cart) {
418 53
                    $cartKeys[] = $Cart->getCartKey();
419 53
                }
420 53
                $this->session->set('cart_keys', $cartKeys);
421 53
                $this->carts = $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'DESC']);
422 53
            }
423 53
        }
424
425
        return $this;
426 86
    }
427
428 86
    /**
429 10
     * @param CartItemComparator $cartItemComparator
430
     */
431
    public function setCartItemComparator($cartItemComparator)
432 76
    {
433
        $this->cartItemComparator = $cartItemComparator;
434 25
    }
435
436
    /**
437 51
     * 指定したインデックスにあるカートを優先にする
438
     *
439
     * @param int $index カートのインデックス
440
     */
441
    public function setPrimary($index = 0)
442
    {
443 86
        $Carts = $this->getCarts();
444
        $primary = $Carts[$index];
445 86
        $prev = $Carts[0];
446 51
        array_splice($Carts, 0, 1, [$primary]);
447
        array_splice($Carts, $index, 1, [$prev]);
448
        $this->carts = $Carts;
449 35
        $this->save();
450 17
    }
451
452
    protected function getUser()
453
    {
454 35
        if (null === $token = $this->tokenStorage->getToken()) {
455 35
            return;
456 35
        }
457 35
458
        if (!is_object($user = $token->getUser())) {
459 35
            // e.g. anonymous authentication
460
            return;
461 35
        }
462
463
        return $user;
464
    }
465
466
    /**
467
     * @param string $allocatedId
468
     */
469
    protected function createCartKey($allocatedId, Customer $Customer = null)
470
    {
471
        if ($Customer instanceof Customer) {
472
            return $Customer->getId().'_'.$allocatedId;
473
        }
474
475
        if ($this->session->has('cart_key_prefix')) {
476
            return $this->session->get('cart_key_prefix').'_'.$allocatedId;
477
        }
478
479
        do {
480
            $random = StringUtil::random(32);
481
            $cartKey = $random.'_'.$allocatedId;
482
            $Cart = $this->cartRepository->findOneBy(['cart_key' => $cartKey]);
483
        } while ($Cart);
484
485
        $this->session->set('cart_key_prefix', $random);
486
487
        return $cartKey;
488
    }
489
}
490