CartService::isProductDisplay()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
/*
3
 * This file is part of EC-CUBE
4
 *
5
 * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
6
 *
7
 * http://www.ec-cube.co.jp/
8
 *
9
 * This program is free software; you can redistribute it and/or
10
 * modify it under the terms of the GNU General Public License
11
 * as published by the Free Software Foundation; either version 2
12
 * of the License, or (at your option) any later version.
13
 *
14
 * This program is distributed in the hope that it will be useful,
15
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17
 * GNU General Public License for more details.
18
 *
19
 * You should have received a copy of the GNU General Public License
20
 * along with this program; if not, write to the Free Software
21
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
 */
23
24
25
namespace Eccube\Service;
26
27
use Doctrine\ORM\EntityManager;
28
use Eccube\Common\Constant;
29
use Eccube\Entity\CartItem;
30
use Eccube\Entity\Master\Disp;
31
use Eccube\Entity\ProductClass;
32
use Eccube\Exception\CartException;
33
use Symfony\Component\HttpFoundation\Session\Session;
34
35
class CartService
36
{
37
    /** @var \Eccube\Application */
38
    public $app;
39
40
    /**
41
     * @var Session
42
     */
43
    private $session;
44
45
    /**
46
     * @var EntityManager
47
     */
48
    private $entityManager;
49
50
    /**
51
     * @var \Eccube\Entity\Cart
52
     */
53
    private $cart;
54
55
    /**
56
     * @var \Eccube\Entity\BaseInfo
57
     */
58
    private $BaseInfo;
59
60
    /**
61
     * @var array
62
     */
63
    private $errors = array();
64
65
    private $ProductType = null;
66
67
    /**
68
     * @var array
69
     */
70
    private $messages = array();
71
72
    /**
73
     * @var array
74
     */
75
    private $error;
76
77
    public function __construct(\Eccube\Application $app)
78
    {
79
        $this->app = $app;
80
        $this->session = $app['session'];
81
        $this->entityManager = $app['orm.em'];
82
83
        if ($this->session->has('cart')) {
84
            $this->cart = $this->session->get('cart');
85
        } else {
86
            $this->cart = new \Eccube\Entity\Cart();
87
        }
88
89
        $this->loadProductClassFromCart();
90
91
        $this->BaseInfo = $app['eccube.repository.base_info']->get();
92
    }
93
94
    /**
95
     * カートに保存されている商品の ProductClass エンティティを読み込み、カートへ設定します。
96
     */
97
    protected function loadProductClassFromCart()
98
    {
99
        /* @var $softDeleteFilter \Eccube\Doctrine\Filter\SoftDeleteFilter */
100
        $softDeleteFilter = $this->entityManager->getFilters()->getFilter('soft_delete');
101
        $excludes = $softDeleteFilter->getExcludes();
102
        $softDeleteFilter->setExcludes(array(
103
            'Eccube\Entity\ProductClass',
104
        ));
105
106
        foreach ($this->cart->getCartItems() as $CartItem) {
107
            $this->loadProductClassFromCartItem($CartItem);
108
        }
109
110
        $softDeleteFilter->setExcludes($excludes);
111
    }
112
113
    /**
114
     * CartItem に対応する ProductClass を設定します。
115
     *
116
     * @param CartItem $CartItem
117
     */
118
    protected function loadProductClassFromCartItem(CartItem $CartItem)
119
    {
120
        $ProductClass = $this
121
            ->entityManager
122
            ->getRepository($CartItem->getClassName())
123
            ->find($CartItem->getClassId());
124
125
        $CartItem->setObject($ProductClass);
126
127
        if (is_null($this->ProductType) && $ProductClass->getDelFlg() == Constant::DISABLED) {
128
            $this->setCanAddProductType($ProductClass->getProductType());
129
        }
130
    }
131
132
    public function setCanAddProductType(\Eccube\Entity\Master\ProductType $ProductType)
133
    {
134
        if (is_null($this->ProductType)) {
135
            $this->ProductType = $ProductType;
136
        }
137
138
        return $this;
139
    }
140
141
    public function save()
142
    {
143
        return $this->session->set('cart', $this->cart);
144
    }
145
146
    public function unlock()
147
    {
148
        $this->cart
149
            ->setLock(false)
150
            ->setPreOrderId(null);
151
    }
152
153
    public function lock()
154
    {
155
        $this->cart
156
            ->setLock(true)
157
            ->setPreOrderId(null);
158
    }
159
160
    /**
161
     * @return bool
162
     */
163
    public function isLocked()
164
    {
165
        return $this->cart->getLock();
166
    }
167
168
    /**
169
     * @param  string $pre_order_id
170
     * @return \Eccube\Service\CartService
171
     */
172
    public function setPreOrderId($pre_order_id)
173
    {
174
        $this->cart->setPreOrderId($pre_order_id);
175
176
        return $this;
177
    }
178
179
    /**
180
     * @return string
181
     */
182
    public function getPreOrderId()
183
    {
184
        return $this->cart->getPreOrderId();
185
    }
186
187
    /**
188
     * @return \Eccube\Service\CartService
189
     */
190
    public function clear()
191
    {
192
        $this->cart
193
            ->setPreOrderId(null)
194
            ->setLock(false)
195
            ->clearCartItems();
196
197
        return $this;
198
    }
199
200
    public function getCanAddProductType()
201
    {
202
        return $this->ProductType;
203
    }
204
205
    /**
206
     *
207
     * @param  string $productClassId
208
     * @param  integer $quantity
209
     * @return \Eccube\Service\CartService
210
     */
211
    public function addProduct($productClassId, $quantity = 1)
212
    {
213
        $quantity += $this->getProductQuantity($productClassId);
214
        $this->setProductQuantity($productClassId, $quantity);
215
216
        return $this;
217
    }
218
219
    /**
220
     * @param  string $productClassId
221
     * @return integer
222
     */
223
    public function getProductQuantity($productClassId)
224
    {
225
        $CartItem = $this->cart->getCartItemByIdentifier('Eccube\Entity\ProductClass', (string)$productClassId);
226
        if ($CartItem) {
227
            return $CartItem->getQuantity();
228
        } else {
229
            return 0;
230
        }
231
    }
232
233
    /**
234
     * @param  \Eccube\Entity\ProductClass|integer $ProductClass
235
     * @param  integer $quantity
236
     * @return \Eccube\Service\CartService
237
     * @throws CartException
238
     */
239
    public function setProductQuantity($ProductClass, $quantity)
240
    {
241
        if (!$ProductClass instanceof ProductClass) {
0 ignored issues
show
Bug introduced by
The class Eccube\Entity\ProductClass does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
242
            $ProductClass = $this->entityManager
243
                ->getRepository('Eccube\Entity\ProductClass')
244
                ->find($ProductClass);
245
            if (!$ProductClass) {
246
                throw new CartException('cart.product.delete');
247
            }
248
        }
249
250
        if (!$this->isProductDisplay($ProductClass)) {
251
            throw new CartException('cart.product.not.status');
252
        }
253
254
        $productName = $this->getProductName($ProductClass);
255
256
        // 商品種別に紐づく配送業者を取得
257
        $deliveries = $this->app['eccube.repository.delivery']->getDeliveries($ProductClass->getProductType());
258
259
        if (count($deliveries) == 0) {
260
            // 商品種別が存在しなければエラー
261
            $this->removeProduct($ProductClass->getId());
262
            $this->addError('cart.product.not.producttype', $productName);
263
            throw new CartException('cart.product.not.producttype');
264
        }
265
266
        $this->setCanAddProductType($ProductClass->getProductType());
267
268
        if ($this->BaseInfo->getOptionMultipleShipping() != Constant::ENABLED) {
269
            if (!$this->canAddProduct($ProductClass->getId())) {
270
                // 複数配送対応でなければ商品種別が異なればエラー
271
                throw new CartException('cart.product.type.kind');
272
            }
273
        } else {
274
            // 複数配送の場合、同一支払方法がなければエラー
275
            if (!$this->canAddProductPayment($ProductClass->getProductType())) {
276
                throw new CartException('cart.product.payment.kind');
277
            }
278
        }
279
280
        $tmp_subtotal = 0;
281
        $tmp_quantity = 0;
282
        foreach ($this->getCartObj()->getCartItems() as $cartitem) {
283
            $pc = $cartitem->getObject();
284
            if ($pc->getId() != $ProductClass->getId()) {
285
                // 追加された商品以外のtotal priceをセット
286
                $tmp_subtotal += $cartitem->getTotalPrice();
287
            }
288
        }
289
290
        // 購入金額上限を超えてしまう数量はエラー
291
        $maxQuantity = floor(($this->app['config']['max_total_fee'] - $tmp_subtotal) / $ProductClass->getPrice02IncTax());
292
        if ($quantity > $maxQuantity) {
293
            $this->setError('cart.over.price_limit');
294
            $tmp_quantity += $maxQuantity;
295
        } else {
296
            $tmp_quantity += $quantity;
297
        }
298
299
        if ($tmp_quantity == 0) {
300
            // 数量が0の場合、エラー
301
            throw new CartException('cart.over.price_limit');
302
        }
303
304
        // 制限数チェック(在庫不足の場合は、処理の中でカート内商品を削除している)
305
        $quantity = $this->setProductLimit($ProductClass, $productName, $tmp_quantity);
306
307
		// 新しい数量でカート内商品を登録する
308
        if (0 < $quantity) {
309
            $CartItem = new CartItem();
310
            $CartItem
311
                ->setClassName('Eccube\Entity\ProductClass')
312
                ->setClassId((string)$ProductClass->getId())
313
                ->setPrice($ProductClass->getPrice02IncTax())
314
                ->setQuantity($quantity);
315
316
            $this->cart->setCartItem($CartItem);
317
        }
318
319
        return $this;
320
    }
321
322
    /**
323
     * @param  string $productClassId
324
     * @return boolean
325
     */
326
    public function canAddProduct($productClassId)
327
    {
328
        $ProductClass = $this
329
            ->entityManager
330
            ->getRepository('\Eccube\Entity\ProductClass')
331
            ->find($productClassId);
332
333
        if (!$ProductClass) {
334
            return false;
335
        }
336
337
        $ProductType = $ProductClass->getProductType();
338
339
        return $this->ProductType == $ProductType;
340
    }
341
342
    /**
343
     * @param \Eccube\Entity\Master\ProductType $ProductType
344
     * @return bool
345
     */
346
    public function canAddProductPayment(\Eccube\Entity\Master\ProductType $ProductType)
347
    {
348
        $deliveries = $this
349
            ->entityManager
350
            ->getRepository('\Eccube\Entity\Delivery')
351
            ->findBy(array('ProductType' => $ProductType));
352
353
        // 支払方法を取得
354
        $payments = $this->entityManager->getRepository('Eccube\Entity\Payment')->findAllowedPayments($deliveries);
355
356
        if ($this->getCart()->getTotalPrice() < 1) {
357
            // カートになければ支払方法を全て設定
358
            $this->getCart()->setPayments($payments);
359
360
            return true;
361
        }
362
363
        // カートに存在している支払方法と追加された商品の支払方法チェック
364
        $arr = array();
365
        foreach ($payments as $payment) {
366
            foreach ($this->getCart()->getPayments() as $p) {
367
                if ($payment['id'] == $p['id']) {
368
                    $arr[] = $payment;
369
                    break;
370
                }
371
            }
372
        }
373
374
        if (count($arr) > 0) {
375
            $this->getCart()->setPayments($arr);
376
377
            return true;
378
        }
379
380
        // 支払条件に一致しない
381
        return false;
382
383
    }
384
385
    /**
386
     * カートブロックに表示するカートを取得します。
387
     * ブロックに表示するカートはチェックを行わず、セットされているカートを返します。
388
     *
389
     * @return \Eccube\Entity\Cart
390
     */
391
    public function getCartObj()
392
    {
393
394
        foreach ($this->cart->getCartItems() as $CartItem) {
395
396
            /** @var \Eccube\Entity\ProductClass $ProductClass */
397
            $ProductClass = $CartItem->getObject();
398
            if (!$ProductClass) {
399
                $this->loadProductClassFromCartItem($CartItem);
400
401
                $ProductClass = $CartItem->getObject();
402
            }
403
404
            if ($ProductClass->getDelFlg()) {
405
                // 商品情報が削除されていたらエラー
406
                $this->setError('cart.product.delete');
407
                // カートから削除
408
                $this->removeProduct($ProductClass->getId());
409
            }
410
        }
411
412
        return $this->cart;
413
414
    }
415
416
    /**
417
     * カートを取得します。
418
     *
419
     * @return \Eccube\Entity\Cart
420
     */
421
    public function getCart()
422
    {
423
        foreach ($this->cart->getCartItems() as $CartItem) {
424
425
            /** @var \Eccube\Entity\ProductClass $ProductClass */
426
            $ProductClass = $CartItem->getObject();
427
            if (!$ProductClass) {
428
                $this->loadProductClassFromCartItem($CartItem);
429
430
                $ProductClass = $CartItem->getObject();
431
            }
432
433
            if ($ProductClass->getDelFlg() == Constant::DISABLED) {
434
                // 商品情報が有効
435
436
                if (!$this->isProductDisplay($ProductClass)) {
437
                    $this->setError('cart.product.not.status');
438
                } else {
439
440
                    $productName = $this->getProductName($ProductClass);
441
442
                    // 制限数チェック(在庫不足の場合は、処理の中でカート内商品を削除している)
443
                    $quantity = $this->setProductLimit($ProductClass, $productName, $CartItem->getQuantity());
444
445
                    /// 個数が異なれば、新しい数量でカート内商品を更新する
446
                    if ((0 < $quantity) && ($CartItem->getQuantity() != $quantity)) {
447
                        // 個数が異なれば更新
448
                        $CartItem->setQuantity($quantity);
449
                        $this->cart->setCartItem($CartItem);
450
                    }
451
                }
452
453
            } else {
454
                // 商品情報が削除されていたらエラー
455
                $this->setError('cart.product.delete');
456
                // カートから削除
457
                $this->removeProduct($ProductClass->getId());
458
            }
459
        }
460
461
        return $this->cart;
462
    }
463
464
    /**
465
     * @param  string $productClassId
466
     * @return \Eccube\Service\CartService
467
     */
468
    public function removeProduct($productClassId)
469
    {
470
        $this->cart->removeCartItemByIdentifier('Eccube\Entity\ProductClass', (string)$productClassId);
471
472
        // 支払方法の再設定
473
        if ($this->BaseInfo->getOptionMultipleShipping() == Constant::ENABLED) {
474
475
            // 複数配送対応
476
            $productTypes = array();
477
            foreach ($this->getCart()->getCartItems() as $item) {
478
                /* @var $ProductClass \Eccube\Entity\ProductClass */
479
                $ProductClass = $item->getObject();
480
                $productTypes[] = $ProductClass->getProductType();
481
            }
482
483
            // 配送業者を取得
484
            $deliveries = $this->entityManager->getRepository('Eccube\Entity\Delivery')->getDeliveries($productTypes);
485
486
            // 支払方法を取得
487
            $payments = $this->entityManager->getRepository('Eccube\Entity\Payment')->findAllowedPayments($deliveries);
488
489
            $this->getCart()->setPayments($payments);
490
        }
491
492
        return $this;
493
    }
494
495
    /**
496
     * @param  string $error
497
     * @param  string $productName
498
     * @return \Eccube\Service\CartService
499
     */
500
    public function addError($error = null, $productName = null)
501
    {
502
        // Filter duplicate
503
        $arrError = $this->session->getFlashBag()->peek('eccube.front.request.error');
504
        $arrProduct = $this->session->getFlashBag()->peek('eccube.front.request.product');
505
        if (in_array($error, $arrError) && in_array($productName, $arrProduct)) {
506
            return $this;
507
        }
508
509
        $this->errors[] = $error;
510
        $this->session->getFlashBag()->add('eccube.front.request.error', $error);
511
        if (!is_null($productName)) {
512
            // 追加されているエラーのキーを取得する
513
            $cnt = $this->session->getFlashBag()->peek('eccube.front.request.error');
514
            end($cnt);
515
            $key = key($cnt);
516
            // エラーと同じキー商品名を設定する
517
            $arrProduct[$key] = $productName;
518
            $this->session->getFlashBag()->set('eccube.front.request.product', $arrProduct);
519
        }
520
521
        return $this;
522
    }
523
524
    /**
525
     * @param  string $productClassId
526
     * @return \Eccube\Service\CartService
527
     */
528
    public function upProductQuantity($productClassId)
529
    {
530
        $quantity = $this->getProductQuantity($productClassId) + 1;
531
        $this->setProductQuantity($productClassId, $quantity);
532
533
        return $this;
534
    }
535
536
    /**
537
     * @param  string $productClassId
538
     * @return \Eccube\Service\CartService
539
     */
540
    public function downProductQuantity($productClassId)
541
    {
542
        $quantity = $this->getProductQuantity($productClassId) - 1;
543
        if ($quantity > 0) {
544
            $this->setProductQuantity($productClassId, $quantity);
545
        }
546
547
        return $this;
548
    }
549
550
    /**
551
     * @return array
552
     */
553
    public function getProductTypes()
554
    {
555
556
        $productTypes = array();
557
        foreach ($this->getCart()->getCartItems() as $item) {
558
            /* @var $ProductClass \Eccube\Entity\ProductClass */
559
            $ProductClass = $item->getObject();
560
            $productTypes[] = $ProductClass->getProductType();
561
        }
562
563
        return array_unique($productTypes);
564
565
    }
566
567
    /**
568
     * @return string[]
569
     */
570
    public function getErrors()
571
    {
572
        return $this->errors;
573
    }
574
575
    /**
576
     * @return string[]
577
     */
578
    public function getMessages()
579
    {
580
        return $this->messages;
581
    }
582
583
    /**
584
     * @param  string $message
585
     * @return \Eccube\Service\CartService
586
     */
587
    public function setMessage($message)
588
    {
589
        $this->messages[] = $message;
590
591
        return $this;
592
    }
593
594
    /**
595
     * @return string
596
     */
597
    public function getError()
598
    {
599
        return $this->error;
600
    }
601
602
    /**
603
     * @param  string $error
604
     * @return \Eccube\Service\CartService
605
     */
606
    public function setError($error = null)
607
    {
608
        $this->error = $error;
0 ignored issues
show
Documentation Bug introduced by
It seems like $error of type string or null is incompatible with the declared type array of property $error.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
609
        $this->session->getFlashBag()->set('eccube.front.request.error', $error);
610
611
        return $this;
612
    }
613
614
    /**
615
     * 商品名を取得
616
     *
617
     * @param ProductClass $ProductClass
618
     * @return string
619
     */
620
    private function getProductName(ProductClass $ProductClass)
621
    {
622
623
        $productName = $ProductClass->getProduct()->getName();
624
625
        if ($ProductClass->hasClassCategory1()) {
626
            $productName .= " - ".$ProductClass->getClassCategory1()->getName();
627
        }
628
629
        if ($ProductClass->hasClassCategory2()) {
630
            $productName .= " - ".$ProductClass->getClassCategory2()->getName();
631
        }
632
633
        return $productName;
634
    }
635
636
637
    /**
638
     * 非公開商品の場合、カートから削除
639
     *
640
     * @param ProductClass $ProductClass
641
     * @return bool
642
     */
643
    private function isProductDisplay(ProductClass $ProductClass)
644
    {
645
646
        if ($ProductClass->getProduct()->getStatus()->getId() !== Disp::DISPLAY_SHOW) {
647
            // 非公開の商品はカートから削除
648
            $this->removeProduct($ProductClass->getId());
649
650
            return false;
651
        }
652
653
        return true;
654
    }
655
656
657
    /**
658
     * 在庫数と販売制限数のチェック
659
     * 在庫数または販売制限数以上の個数が設定されていれば、それぞれの個数にセットし、
660
     * 在庫数と販売制限数ともに個数が超えていれば、少ない方を適用させてメッセージを表示する
661
     *
662
     * @param ProductClass $ProductClass
663
     * @param $productName
664
     * @param $quantity
665
     * @return int チェック後に更新した個数
666
     */
667
    private function setProductLimit(ProductClass $ProductClass, $productName, $quantity)
668
    {
669
670
        /**
671
         * 実際の在庫は ProductClass::ProductStock だが、購入時にロックがかかるため、
672
         * ここでは ProductClass::stock で在庫のチェックをする
673
         */
674
675
        // 在庫数(在庫無制限の場合、null)
676
        $stock = $ProductClass->getStock();
677
        // 在庫無制限(在庫無制限の場合、1)
678
        $stockUnlimited = $ProductClass->getStockUnlimited();
679
680
        // 販売制限数(設定されていなければnull)
681
        $saleLimit = $ProductClass->getSaleLimit();
682
683
        if ($stockUnlimited) {
684
            // 在庫無制限
685
686
            if ($saleLimit && $saleLimit < $quantity) {
687
                // 販売制限数を超えていれば販売制限数をセット
688
                $this->addError('cart.over.sale_limit', $productName);
689
690
                return $saleLimit;
691
            }
692
        } else {
693
            // 在庫制限あり
694
695
            if ($stock < 1) {
696
                // 在庫がなければカートから削除
697
                $this->addError('cart.zero.stock', $productName);
698
                $this->removeProduct($ProductClass->getId());
699
700
                return 0;
701
            } else {
702
                // 在庫数チェックと販売制限数チェックどちらを適用するか設定
703
                $message = 'cart.over.stock';
704
                if ($saleLimit) {
705
                    if ($stock > $saleLimit) {
706
                        // 販売制限数チェック
707
                        $limit = $saleLimit;
708
                        $message = 'cart.over.sale_limit';
709
                    } else {
710
                        // 在庫数チェック
711
                        $limit = $stock;
712
                    }
713
                } else {
714
                    // 在庫数チェック
715
                    $limit = $stock;
716
                }
717
718
                if ($limit < $quantity) {
719
                    // 在庫数、販売制限数を超えていれば購入可能数までをセット
720
                    $this->addError($message, $productName);
721
722
                    return $limit;
723
                }
724
            }
725
        }
726
727
        return $quantity;
728
    }
729
730
}
731