Completed
Pull Request — experimental/sf (#3460)
by Kiyotaka
260:03 queued 194:55
created

CartService::createCartKey()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

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