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
|
|
|
|
32
|
|
|
class CartService |
33
|
|
|
{ |
34
|
|
|
/** |
35
|
|
|
* @var Cart[] |
36
|
|
|
*/ |
37
|
|
|
protected $carts; |
38
|
|
|
|
39
|
|
|
/** |
40
|
|
|
* @var SessionInterface |
41
|
|
|
*/ |
42
|
|
|
protected $session; |
43
|
|
|
|
44
|
|
|
/** |
45
|
|
|
* @var \Doctrine\ORM\EntityManagerInterface |
46
|
|
|
*/ |
47
|
|
|
protected $entityManager; |
48
|
|
|
|
49
|
|
|
/** |
50
|
|
|
* @var ItemHolderInterface |
51
|
|
|
* |
52
|
|
|
* @deprecated |
53
|
|
|
*/ |
54
|
|
|
protected $cart; |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* @var ProductClassRepository |
58
|
|
|
*/ |
59
|
|
|
protected $productClassRepository; |
60
|
|
|
|
61
|
|
|
/** |
62
|
|
|
* @var CartRepository |
63
|
|
|
*/ |
64
|
|
|
protected $cartRepository; |
65
|
|
|
|
66
|
|
|
/** |
67
|
|
|
* @var CartItemComparator |
68
|
|
|
*/ |
69
|
|
|
protected $cartItemComparator; |
70
|
|
|
|
71
|
|
|
/** |
72
|
|
|
* @var CartItemAllocator |
73
|
|
|
*/ |
74
|
|
|
protected $cartItemAllocator; |
75
|
|
|
|
76
|
|
|
/** |
77
|
|
|
* @var OrderHelper |
78
|
|
|
*/ |
79
|
|
|
protected $orderHelper; |
80
|
|
|
|
81
|
|
|
/** |
82
|
|
|
* @var OrderRepository |
83
|
|
|
*/ |
84
|
|
|
protected $orderRepository; |
85
|
|
|
|
86
|
|
|
/** |
87
|
|
|
* @var TokenStorageInterface |
88
|
|
|
*/ |
89
|
|
|
protected $tokenStorage; |
90
|
|
|
|
91
|
|
|
/** |
92
|
|
|
* CartService constructor. |
93
|
|
|
* |
94
|
|
|
* @param SessionInterface $session |
95
|
|
|
* @param EntityManagerInterface $entityManager |
96
|
|
|
* @param ProductClassRepository $productClassRepository |
97
|
|
|
* @param CartRepository $cartRepository |
98
|
|
|
* @param CartItemComparator $cartItemComparator |
99
|
|
|
* @param CartItemAllocator $cartItemAllocator |
100
|
|
|
* @param OrderHelper $orderHelper |
101
|
|
|
* @param OrderRepository $orderRepository |
102
|
|
|
* @param TokenStorageInterface $tokenStorage |
103
|
|
|
*/ |
104
|
|
|
public function __construct( |
105
|
|
|
SessionInterface $session, |
106
|
|
|
EntityManagerInterface $entityManager, |
107
|
|
|
ProductClassRepository $productClassRepository, |
108
|
|
|
CartRepository $cartRepository, |
109
|
|
|
CartItemComparator $cartItemComparator, |
110
|
|
|
CartItemAllocator $cartItemAllocator, |
111
|
|
|
OrderHelper $orderHelper, |
112
|
|
|
OrderRepository $orderRepository, |
113
|
|
|
TokenStorageInterface $tokenStorage |
114
|
|
|
) { |
115
|
|
|
$this->session = $session; |
116
|
|
|
$this->entityManager = $entityManager; |
117
|
|
|
$this->productClassRepository = $productClassRepository; |
118
|
|
|
$this->cartRepository = $cartRepository; |
119
|
|
|
$this->cartItemComparator = $cartItemComparator; |
120
|
|
|
$this->cartItemAllocator = $cartItemAllocator; |
121
|
|
|
$this->orderHelper = $orderHelper; |
122
|
|
|
$this->orderRepository = $orderRepository; |
123
|
|
|
$this->tokenStorage = $tokenStorage; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
public function getCarts() |
127
|
|
|
{ |
128
|
|
|
if (!empty($this->carts)) { |
129
|
|
|
return $this->carts; |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
if ($this->getUser()) { |
133
|
|
|
$this->carts = $this->getPersistedCarts(); |
134
|
|
|
} else { |
135
|
|
|
$this->carts = $this->getSessionCarts(); |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
return $this->carts; |
139
|
|
|
} |
140
|
|
|
|
141
|
|
|
/** |
142
|
|
|
* 永続化されたカートを返す |
143
|
|
|
* |
144
|
|
|
* @return Cart[] |
145
|
|
|
*/ |
146
|
|
|
public function getPersistedCarts() |
147
|
|
|
{ |
148
|
|
|
return $this->cartRepository->findBy(['Customer' => $this->getUser()]); |
149
|
|
|
} |
150
|
|
|
|
151
|
|
|
/** |
152
|
|
|
* セッションにあるカートを返す |
153
|
|
|
* |
154
|
|
|
* @return Cart[] |
155
|
|
|
*/ |
156
|
|
|
public function getSessionCarts() |
157
|
|
|
{ |
158
|
|
|
$cartKeys = $this->session->get('cart_keys', []); |
159
|
|
|
|
160
|
|
|
return $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'DESC']); |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
/** |
164
|
|
|
* 会員が保持する永続化されたカートと、非会員時のカートをマージする. |
165
|
|
|
*/ |
166
|
|
|
public function mergeFromPersistedCart() |
167
|
|
|
{ |
168
|
|
|
$CartItems = []; |
169
|
|
|
foreach ($this->getPersistedCarts() as $Cart) { |
170
|
|
|
$CartItems = $this->mergeCartItems($Cart->getCartItems(), $CartItems); |
171
|
|
|
} |
172
|
|
|
|
173
|
|
|
// セッションにある非会員カートとDBから取得した会員カートをマージする. |
174
|
|
|
foreach ($this->getSessionCarts() as $Cart) { |
175
|
|
|
$CartItems = $this->mergeCartItems($Cart->getCartItems(), $CartItems); |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
$this->restoreCarts($CartItems); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* @return ItemHolderInterface|Cart |
183
|
|
|
*/ |
184
|
|
|
public function getCart() |
185
|
|
|
{ |
186
|
|
|
$Carts = $this->getCarts(); |
187
|
|
|
|
188
|
|
|
if (empty($Carts)) { |
189
|
|
|
return null; |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
$cartKeys = $this->session->get('cart_keys', []); |
193
|
|
|
$Cart = null; |
194
|
|
|
if (count($cartKeys) > 0) { |
195
|
|
|
foreach ($Carts as $cart) { |
196
|
|
|
if ($cart->getCartKey() === current($cartKeys)) { |
197
|
|
|
$Cart = $cart; |
198
|
|
|
break; |
199
|
|
|
} |
200
|
|
|
} |
201
|
|
|
} else { |
202
|
|
|
$Cart = current($Carts); |
203
|
|
|
} |
204
|
|
|
|
205
|
|
|
return $Cart; |
206
|
|
|
} |
207
|
|
|
|
208
|
|
|
/** |
209
|
|
|
* @param CartItem[] $cartItems |
210
|
|
|
* |
211
|
|
|
* @return CartItem[] |
212
|
|
|
*/ |
213
|
|
|
protected function mergeAllCartItems($cartItems = []) |
214
|
|
|
{ |
215
|
|
|
/** @var CartItem[] $allCartItems */ |
216
|
|
|
$allCartItems = []; |
217
|
|
|
|
218
|
|
|
foreach ($this->getCarts() as $Cart) { |
219
|
|
|
$allCartItems = $this->mergeCartItems($Cart->getCartItems(), $allCartItems); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
return $this->mergeCartItems($cartItems, $allCartItems); |
223
|
|
|
} |
224
|
|
|
|
225
|
|
|
/** |
226
|
|
|
* @param $cartItems |
227
|
|
|
* @param $allCartItems |
228
|
|
|
* |
229
|
|
|
* @return array |
230
|
|
|
*/ |
231
|
|
|
protected function mergeCartItems($cartItems, $allCartItems) |
232
|
|
|
{ |
233
|
|
|
foreach ($cartItems as $item) { |
234
|
|
|
$itemExists = false; |
235
|
|
|
foreach ($allCartItems as $itemInArray) { |
236
|
|
|
// 同じ明細があればマージする |
237
|
|
|
if ($this->cartItemComparator->compare($item, $itemInArray)) { |
238
|
|
|
$itemInArray->setQuantity($itemInArray->getQuantity() + $item->getQuantity()); |
239
|
|
|
$itemExists = true; |
240
|
|
|
break; |
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
if (!$itemExists) { |
244
|
|
|
$allCartItems[] = $item; |
245
|
|
|
} |
246
|
|
|
} |
247
|
|
|
|
248
|
|
|
return $allCartItems; |
249
|
|
|
} |
250
|
|
|
|
251
|
|
|
protected function restoreCarts($cartItems) |
252
|
|
|
{ |
253
|
|
|
foreach ($this->getCarts() as $Cart) { |
254
|
|
|
foreach ($Cart->getCartItems() as $i) { |
255
|
|
|
$this->entityManager->remove($i); |
256
|
|
|
$this->entityManager->flush($i); |
|
|
|
|
257
|
|
|
} |
258
|
|
|
$this->entityManager->remove($Cart); |
259
|
|
|
$this->entityManager->flush($Cart); |
|
|
|
|
260
|
|
|
} |
261
|
|
|
$this->carts = []; |
262
|
|
|
|
263
|
|
|
/** @var Cart[] $Carts */ |
264
|
|
|
$Carts = []; |
265
|
|
|
|
266
|
|
|
foreach ($cartItems as $item) { |
267
|
|
|
$allocatedId = $this->cartItemAllocator->allocate($item); |
268
|
|
|
$cartKey = $this->createCartKey($allocatedId, $this->getUser()); |
269
|
|
|
|
270
|
|
|
if (isset($Carts[$cartKey])) { |
271
|
|
|
$Cart = $Carts[$cartKey]; |
272
|
|
|
$Cart->addCartItem($item); |
273
|
|
|
$item->setCart($Cart); |
274
|
|
|
} else { |
275
|
|
|
/** @var Cart $Cart */ |
276
|
|
|
$Cart = $this->cartRepository->findOneBy(['cart_key' => $cartKey]); |
277
|
|
|
if ($Cart) { |
278
|
|
|
foreach ($Cart->getCartItems() as $i) { |
279
|
|
|
$this->entityManager->remove($i); |
280
|
|
|
$this->entityManager->flush($i); |
|
|
|
|
281
|
|
|
} |
282
|
|
|
$this->entityManager->remove($Cart); |
283
|
|
|
$this->entityManager->flush($Cart); |
|
|
|
|
284
|
|
|
} |
285
|
|
|
$Cart = new Cart(); |
286
|
|
|
$Cart->setCartKey($cartKey); |
287
|
|
|
$Cart->addCartItem($item); |
288
|
|
|
$item->setCart($Cart); |
289
|
|
|
$Carts[$cartKey] = $Cart; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
$this->carts = array_values($Carts); |
294
|
|
|
} |
295
|
|
|
|
296
|
|
|
/** |
297
|
|
|
* カートに商品を追加します. |
298
|
|
|
* |
299
|
|
|
* @param $ProductClass ProductClass 商品規格 |
300
|
|
|
* @param $quantity int 数量 |
301
|
|
|
* |
302
|
|
|
* @return bool 商品を追加できた場合はtrue |
303
|
|
|
*/ |
304
|
|
|
public function addProduct($ProductClass, $quantity = 1) |
305
|
|
|
{ |
306
|
|
View Code Duplication |
if (!$ProductClass instanceof ProductClass) { |
|
|
|
|
307
|
|
|
$ProductClassId = $ProductClass; |
308
|
|
|
$ProductClass = $this->entityManager |
309
|
|
|
->getRepository(ProductClass::class) |
310
|
|
|
->find($ProductClassId); |
311
|
|
|
if (is_null($ProductClass)) { |
312
|
|
|
return false; |
313
|
|
|
} |
314
|
|
|
} |
315
|
|
|
|
316
|
|
|
$ClassCategory1 = $ProductClass->getClassCategory1(); |
317
|
|
|
if ($ClassCategory1 && !$ClassCategory1->isVisible()) { |
318
|
|
|
return false; |
319
|
|
|
} |
320
|
|
|
$ClassCategory2 = $ProductClass->getClassCategory2(); |
321
|
|
|
if ($ClassCategory2 && !$ClassCategory2->isVisible()) { |
322
|
|
|
return false; |
323
|
|
|
} |
324
|
|
|
|
325
|
|
|
$newItem = new CartItem(); |
326
|
|
|
$newItem->setQuantity($quantity); |
327
|
|
|
$newItem->setPrice($ProductClass->getPrice02IncTax()); |
328
|
|
|
$newItem->setProductClass($ProductClass); |
329
|
|
|
|
330
|
|
|
$allCartItems = $this->mergeAllCartItems([$newItem]); |
331
|
|
|
$this->restoreCarts($allCartItems); |
332
|
|
|
|
333
|
|
|
return true; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
public function removeProduct($ProductClass) |
337
|
|
|
{ |
338
|
|
View Code Duplication |
if (!$ProductClass instanceof ProductClass) { |
|
|
|
|
339
|
|
|
$ProductClassId = $ProductClass; |
340
|
|
|
$ProductClass = $this->entityManager |
341
|
|
|
->getRepository(ProductClass::class) |
342
|
|
|
->find($ProductClassId); |
343
|
|
|
if (is_null($ProductClass)) { |
344
|
|
|
return false; |
345
|
|
|
} |
346
|
|
|
} |
347
|
|
|
|
348
|
|
|
$removeItem = new CartItem(); |
349
|
|
|
$removeItem->setPrice($ProductClass->getPrice02IncTax()); |
350
|
|
|
$removeItem->setProductClass($ProductClass); |
351
|
|
|
|
352
|
|
|
$allCartItems = $this->mergeAllCartItems(); |
353
|
|
|
$foundIndex = -1; |
354
|
|
|
foreach ($allCartItems as $index => $itemInCart) { |
355
|
|
|
if ($this->cartItemComparator->compare($itemInCart, $removeItem)) { |
356
|
|
|
$foundIndex = $index; |
357
|
|
|
break; |
358
|
|
|
} |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
array_splice($allCartItems, $foundIndex, 1); |
362
|
|
|
$this->restoreCarts($allCartItems); |
363
|
|
|
|
364
|
|
|
return true; |
365
|
|
|
} |
366
|
|
|
|
367
|
|
|
public function save() |
368
|
|
|
{ |
369
|
|
|
$cartKeys = []; |
370
|
|
|
foreach ($this->carts as $Cart) { |
371
|
|
|
$Cart->setCustomer($this->getUser()); |
372
|
|
|
$this->entityManager->persist($Cart); |
373
|
|
|
foreach ($Cart->getCartItems() as $item) { |
374
|
|
|
$this->entityManager->persist($item); |
375
|
|
|
$this->entityManager->flush($item); |
|
|
|
|
376
|
|
|
} |
377
|
|
|
$this->entityManager->flush($Cart); |
|
|
|
|
378
|
|
|
$cartKeys[] = $Cart->getCartKey(); |
379
|
|
|
} |
380
|
|
|
|
381
|
|
|
$this->session->set('cart_keys', $cartKeys); |
382
|
|
|
|
383
|
|
|
return; |
384
|
|
|
} |
385
|
|
|
|
386
|
|
|
/** |
387
|
|
|
* @param string $pre_order_id |
388
|
|
|
* |
389
|
|
|
* @return \Eccube\Service\CartService |
390
|
|
|
*/ |
391
|
|
|
public function setPreOrderId($pre_order_id) |
392
|
|
|
{ |
393
|
|
|
$this->getCart()->setPreOrderId($pre_order_id); |
394
|
|
|
|
395
|
|
|
return $this; |
396
|
|
|
} |
397
|
|
|
|
398
|
|
|
/** |
399
|
|
|
* @return string |
400
|
|
|
*/ |
401
|
|
|
public function getPreOrderId() |
402
|
|
|
{ |
403
|
|
|
return $this->getCart()->getPreOrderId(); |
404
|
|
|
} |
405
|
|
|
|
406
|
|
|
/** |
407
|
|
|
* @return \Eccube\Service\CartService |
408
|
|
|
*/ |
409
|
|
|
public function clear() |
410
|
|
|
{ |
411
|
|
|
$Carts = $this->getCarts(); |
412
|
|
|
if (!empty($Carts)) { |
413
|
|
|
$removed = $this->getCart(); |
414
|
|
|
if ($removed && UnitOfWork::STATE_MANAGED === $this->entityManager->getUnitOfWork()->getEntityState($removed)) { |
415
|
|
|
$this->entityManager->remove($removed); |
416
|
|
|
$this->entityManager->flush($removed); |
|
|
|
|
417
|
|
|
|
418
|
|
|
$cartKeys = []; |
419
|
|
|
foreach ($Carts as $key => $Cart) { |
420
|
|
|
// テーブルから削除されたカートを除外する |
421
|
|
|
if ($Cart == $removed) { |
422
|
|
|
unset($Carts[$key]); |
423
|
|
|
} |
424
|
|
|
$cartKeys[] = $Cart->getCartKey(); |
425
|
|
|
} |
426
|
|
|
$this->session->set('cart_keys', $cartKeys); |
427
|
|
|
// 注文完了のカートキーをセッションから削除する |
428
|
|
|
$this->session->remove('cart_key'); |
429
|
|
|
$this->carts = $this->cartRepository->findBy(['cart_key' => $cartKeys], ['id' => 'DESC']); |
430
|
|
|
} |
431
|
|
|
} |
432
|
|
|
|
433
|
|
|
return $this; |
434
|
|
|
} |
435
|
|
|
|
436
|
|
|
/** |
437
|
|
|
* @param CartItemComparator $cartItemComparator |
438
|
|
|
*/ |
439
|
|
|
public function setCartItemComparator($cartItemComparator) |
440
|
|
|
{ |
441
|
|
|
$this->cartItemComparator = $cartItemComparator; |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
/** |
445
|
|
|
* カートキーで指定したインデックスにあるカートを優先にする |
446
|
|
|
* |
447
|
|
|
* @param string $cartKey カートキー |
448
|
|
|
*/ |
449
|
|
|
public function setPrimary($cartKey) |
450
|
|
|
{ |
451
|
|
|
$Carts = $this->getCarts(); |
452
|
|
|
$primary = $Carts[0]; |
453
|
|
|
$index = 0; |
454
|
|
|
foreach ($Carts as $key => $Cart) { |
455
|
|
|
if ($Cart->getCartKey() === $cartKey) { |
456
|
|
|
$index = $key; |
457
|
|
|
$primary = $Carts[$index]; |
458
|
|
|
break; |
459
|
|
|
} |
460
|
|
|
} |
461
|
|
|
$prev = $Carts[0]; |
462
|
|
|
array_splice($Carts, 0, 1, [$primary]); |
463
|
|
|
array_splice($Carts, $index, 1, [$prev]); |
464
|
|
|
$this->carts = $Carts; |
465
|
|
|
$this->save(); |
466
|
|
|
} |
467
|
|
|
|
468
|
|
|
protected function getUser() |
469
|
|
|
{ |
470
|
|
|
if (null === $token = $this->tokenStorage->getToken()) { |
471
|
|
|
return; |
472
|
|
|
} |
473
|
|
|
|
474
|
|
|
if (!is_object($user = $token->getUser())) { |
475
|
|
|
// e.g. anonymous authentication |
476
|
|
|
return; |
477
|
|
|
} |
478
|
|
|
|
479
|
|
|
return $user; |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
/** |
483
|
|
|
* @param string $allocatedId |
484
|
|
|
* |
485
|
|
|
* @return string |
486
|
|
|
*/ |
487
|
|
|
protected function createCartKey($allocatedId, Customer $Customer = null) |
488
|
|
|
{ |
489
|
|
|
if ($Customer instanceof Customer) { |
490
|
|
|
return $Customer->getId().'_'.$allocatedId; |
491
|
|
|
} |
492
|
|
|
|
493
|
|
|
if ($this->session->has('cart_key_prefix')) { |
494
|
|
|
return $this->session->get('cart_key_prefix').'_'.$allocatedId; |
495
|
|
|
} |
496
|
|
|
|
497
|
|
|
do { |
498
|
|
|
$random = StringUtil::random(32); |
499
|
|
|
$cartKey = $random.'_'.$allocatedId; |
500
|
|
|
$Cart = $this->cartRepository->findOneBy(['cart_key' => $cartKey]); |
501
|
|
|
} while ($Cart); |
502
|
|
|
|
503
|
|
|
$this->session->set('cart_key_prefix', $random); |
504
|
|
|
|
505
|
|
|
return $cartKey; |
506
|
|
|
} |
507
|
|
|
} |
508
|
|
|
|
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.