Failed Conditions
Pull Request — 4.0 (#3650)
by chihiro
12:55 queued 06:00
created

OrderHelper::verifyCart()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 4

Importance

Changes 0
Metric Value
cc 4
nc 3
nop 1
dl 0
loc 17
ccs 11
cts 11
cp 1
crap 4
rs 9.7
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 Eccube\Entity\Cart;
18
use Eccube\Entity\Customer;
19
use Eccube\Entity\Master\DeviceType;
20
use Eccube\Entity\Master\OrderItemType;
21
use Eccube\Entity\Master\OrderStatus;
22
use Eccube\Entity\Order;
23
use Eccube\Entity\OrderItem;
24
use Eccube\Entity\Shipping;
25
use Eccube\EventListener\SecurityListener;
26
use Eccube\Repository\DeliveryRepository;
27
use Eccube\Repository\Master\DeviceTypeRepository;
28
use Eccube\Repository\Master\OrderItemTypeRepository;
29
use Eccube\Repository\Master\OrderStatusRepository;
30
use Eccube\Repository\Master\PrefRepository;
31
use Eccube\Repository\OrderRepository;
32
use Eccube\Repository\PaymentRepository;
33
use Eccube\Util\StringUtil;
34
use SunCat\MobileDetectBundle\DeviceDetector\MobileDetector;
35
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
36
use Symfony\Component\DependencyInjection\ContainerInterface;
37
use Symfony\Component\HttpFoundation\Session\SessionInterface;
38
39
class OrderHelper
40
{
41
    // FIXME 必要なメソッドのみ移植する
42
    use ControllerTrait;
43
44
    /**
45
     * @var ContainerInterface
46
     */
47
    protected $container;
48
49
    /**
50
     * @var string 非会員情報を保持するセッションのキー
51
     */
52
    const SESSION_NON_MEMBER = 'eccube.front.shopping.nonmember';
53
54
    /**
55
     * @var string 非会員の住所情報を保持するセッションのキー
56
     */
57
    const SESSION_NON_MEMBER_ADDRESSES = 'eccube.front.shopping.nonmember.customeraddress';
58
59
    /**
60
     * @var string 受注IDを保持するセッションのキー
61
     */
62
    const SESSION_ORDER_ID = 'eccube.front.shopping.order.id';
63
64
    /**
65
     * @var string カートが分割されているかどうかのフラグ. 購入フローからのログイン時にカートが分割された場合にtrueがセットされる.
66
     *
67
     * @see SecurityListener
68
     */
69
    const SESSION_CART_DEVIDE_FLAG = 'eccube.front.cart.divide';
70
71
    /**
72
     * @var SessionInterface
73
     */
74
    protected $session;
75
76
    /**
77
     * @var PrefRepository
78
     */
79
    protected $prefRepository;
80
81
    /**
82
     * @var OrderRepository
83
     */
84
    protected $orderRepository;
85
86
    /**
87
     * @var OrderItemTypeRepository
88
     */
89
    protected $orderItemTypeRepository;
90
91
    public function __construct(
92
        ContainerInterface $container,
93
        EntityManagerInterface $entityManager,
94
        OrderRepository $orderRepository,
95
        OrderItemTypeRepository $orderItemTypeRepository,
96
        OrderStatusRepository $orderStatusRepository,
97
        DeliveryRepository $deliveryRepository,
98
        PaymentRepository $paymentRepository,
99
        DeviceTypeRepository $deviceTypeRepository,
100
        PrefRepository $prefRepository,
101
        MobileDetector $mobileDetector,
102
        SessionInterface $session
103
    ) {
104
        $this->container = $container;
105
        $this->orderRepository = $orderRepository;
106
        $this->orderStatusRepository = $orderStatusRepository;
107
        $this->orderItemTypeRepository = $orderItemTypeRepository;
108
        $this->deliveryRepository = $deliveryRepository;
109
        $this->paymentRepository = $paymentRepository;
110
        $this->deviceTypeRepository = $deviceTypeRepository;
111
        $this->entityManager = $entityManager;
112
        $this->prefRepository = $prefRepository;
113
        $this->mobileDetector = $mobileDetector;
114
        $this->session = $session;
115 175
    }
116
117
    /**
118
     * 購入処理中の受注を生成する.
119
     *
120
     * @param Customer $Customer
121
     * @param $CartItems
122
     * @return Order
123
     */
124
    public function createPurchaseProcessingOrder(Cart $Cart, Customer $Customer)
125
    {
126
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
127
        $Order = new Order($OrderStatus);
128 175
129 175
        $preOrderId = $this->createPreOrderId();
130 175
        $Order->setPreOrderId($preOrderId);
131 175
132 175
        // 顧客情報の設定
133 175
        $this->setCustomer($Order, $Customer);
134 175
135 175
        $DeviceType = $this->deviceTypeRepository->find($this->mobileDetector->isMobile() ? DeviceType::DEVICE_TYPE_MB : DeviceType::DEVICE_TYPE_PC);
136 175
        $Order->setDeviceType($DeviceType);
137 175
138 175
        // 明細情報の設定
139
        $OrderItems = $this->createOrderItemsFromCartItems($Cart->getCartItems());
0 ignored issues
show
Documentation introduced by
$Cart->getCartItems() is of type object<Doctrine\Common\C...ccube\Entity\CartItem>>, but the function expects a object<Eccube\Service\ArrayCollection>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
140
        $OrderItemsGroupBySaleType = array_reduce($OrderItems, function ($result, $item) {
141
            /* @var OrderItem $item */
142
            $saleTypeId = $item->getProductClass()->getSaleType()->getId();
143
            $result[$saleTypeId][] = $item;
144
145
            return $result;
146
        }, []);
147
148
        foreach ($OrderItemsGroupBySaleType as $OrderItems) {
149 51
            $Shipping = $this->createShippingFromCustomer($Customer);
150
            $Shipping->setOrder($Order);
151 51
            $this->addOrderItems($Order, $Shipping, $OrderItems);
152 51
            $this->setDefaultDelivery($Shipping);
153
            $this->entityManager->persist($Shipping);
154 51
            $Order->addShipping($Shipping);
155
        }
156 51
157
        $this->setDefaultPayment($Order);
158
159
        $this->entityManager->persist($Order);
160 51
161
        return $Order;
162 51
    }
163 51
164
    /**
165
     * カーとの整合性チェック.
166 51
     *
167 51
     * @param Cart $Cart
168
     *
169 51
     * @return bool カートに商品が入っていない場合, またはログイン時にカートが分割されている場合にtrue
170 51
     */
171
    public function verifyCart(Cart $Cart = null)
172 51
    {
173 51
        if ($Cart && count($Cart->getCartItems()) > 0) {
174
            $divide = $this->session->get(self::SESSION_CART_DEVIDE_FLAG);
175 51
            if ($divide) {
176 51
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
177 51
178 51
                return false;
179 51
            }
180 51
181 51
            return true;
182
        }
183
184 51
        log_info('カートに商品が入っていません。');
185
186 51
        return false;
187
    }
188
189 51
    /**
190
     * 注文手続き画面でログインが必要かどうかの判定
191
     *
192
     * @return bool
193
     */
194
    public function isLoginRequired()
195
    {
196
        // フォームログイン済はログイン不要
197
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
198
            return false;
199 2
        }
200
201 2
        // Remember Meログイン済の場合はフォームからのログインが必要
202 2
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
203
            return true;
204 2
        }
205 1
206 1
        // 未ログインだがお客様情報を入力している場合はログイン不要
207 1
        if (!$this->getUser() && $this->getNonMember()) {
208 1
            return false;
209 1
        }
210 1
211 1
        return true;
212
    }
213
214 2
    /**
215
     * 購入処理中の受注を取得する.
216
     *
217 51
     * @param null $preOrderId
218
     *
219
     * @return null|Order
220
     */
221 51
    public function getPurchaseProcessingOrder($preOrderId = null)
222
    {
223 51
        if (!$preOrderId) {
224
            return null;
225 51
        }
226
227
        return $this->orderRepository->findOneBy([
228
            'pre_order_id' => $preOrderId,
229 51
            'OrderStatus' => OrderStatus::PROCESSING,
230
        ]);
231 51
    }
232
233
    /**
234 51
     * セッションに保持されている非会員情報を取得する.
235
     * 非会員購入時に入力されたお客様情報を返す.
236 51
     *
237 34
     * @return Customer
238
     */
239 View Code Duplication
    public function getNonMember()
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
240 51
    {
241 51
        $NonMember = $this->session->get(self::SESSION_NON_MEMBER);
242
        if ($NonMember && $NonMember->getPref()) {
243 51
            $Pref = $this->prefRepository->find($NonMember->getPref()->getId());
244
            $NonMember->setPref($Pref);
245
        }
246
247
        return $NonMember;
248
    }
249
250
    /**
251
     * @param Cart $Cart
252
     * @param Customer $Customer
253
     *
254
     * @return Order|null
255
     */
256 51
    public function initializeOrder(Cart $Cart, Customer $Customer)
257
    {
258 51
        // 購入処理中の受注情報を取得
259
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
0 ignored issues
show
Bug introduced by
It seems like $Cart->getPreOrderId() targeting Eccube\Entity\Cart::getPreOrderId() can also be of type string; however, Eccube\Service\OrderHelp...rchaseProcessingOrder() does only seem to accept null, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
260 51
            return $Order;
261
        }
262
263 51
        // 受注情報を作成
264
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
265 51
        $Cart->setPreOrderId($Order->getPreOrderId());
266
267 51
        return $Order;
268
    }
269 51
270 51
    public function removeSession()
271 51
    {
272 51
        $this->session->remove(self::SESSION_ORDER_ID);
273 51
        $this->session->remove(self::SESSION_ORDER_ID);
274 51
        $this->session->remove(self::SESSION_NON_MEMBER);
275 51
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
276
    }
277 51
278 51
    private function createPreOrderId()
279 51
    {
280 51
        // ランダムなpre_order_idを作成
281
        do {
282 51
            $preOrderId = sha1(StringUtil::random(32));
283 51
284 42
            $Order = $this->orderRepository->findOneBy(
285 42
                [
286
                    'pre_order_id' => $preOrderId,
287
                    'OrderStatus' => OrderStatus::PROCESSING,
288 51
                ]
289 51
            );
290
        } while ($Order);
291
292 51
        return $preOrderId;
293
    }
294 51
295
    private function setCustomer(Order $Order, Customer $Customer)
296 51
    {
297 51
        if ($Customer->getId()) {
298 51
            $Order->setCustomer($Customer);
299 51
        }
300 51
301 51
        $Order->copyProperties(
302 51
            $Customer,
303 51
            [
304 51
                'id',
305 51
                'create_date',
306
                'update_date',
307 51
                'del_flg',
308
            ]
309
        );
310 51
    }
311
312
    /**
313 51
     * @param ArrayCollection $CartItems
314 51
     *
315
     * @return OrderItem[]
316 51
     */
317 51
    private function createOrderItemsFromCartItems($CartItems)
318 51
    {
319 51
        $ProductItemType = $this->orderItemTypeRepository->find(OrderItemType::PRODUCT);
320
321
        return array_map(function ($item) use ($ProductItemType) {
322
            /* @var $item CartItem */
323 51
            /* @var $ProductClass \Eccube\Entity\ProductClass */
324
            $ProductClass = $item->getProductClass();
325
            /* @var $Product \Eccube\Entity\Product */
326 51
            $Product = $ProductClass->getProduct();
327 51
328 51
            $OrderItem = new OrderItem();
329
            $OrderItem
330
                ->setProduct($Product)
331 51
                ->setProductClass($ProductClass)
332
                ->setProductName($Product->getName())
333 51
                ->setProductCode($ProductClass->getCode())
334
                ->setPrice($ProductClass->getPrice02())
335
                ->setQuantity($item->getQuantity())
336 51
                ->setOrderItemType($ProductItemType);
0 ignored issues
show
Bug introduced by
It seems like $ProductItemType defined by $this->orderItemTypeRepo...OrderItemType::PRODUCT) on line 319 can also be of type object; however, Eccube\Entity\OrderItem::setOrderItemType() does only seem to accept null|object<Eccube\Entity\Master\OrderItemType>, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
337
338 51
            $ClassCategory1 = $ProductClass->getClassCategory1();
339 51
            if (!is_null($ClassCategory1)) {
340 51
                $OrderItem->setClasscategoryName1($ClassCategory1->getName());
341
                $OrderItem->setClassName1($ClassCategory1->getClassName()->getName());
342
            }
343
            $ClassCategory2 = $ProductClass->getClassCategory2();
344 51
            if (!is_null($ClassCategory2)) {
345 51
                $OrderItem->setClasscategoryName2($ClassCategory2->getName());
346
                $OrderItem->setClassName2($ClassCategory2->getClassName()->getName());
347
            }
348
349 51
            return $OrderItem;
350
        }, $CartItems->toArray());
351
    }
352 51
353
    /**
354
     * @param Customer $Customer
355 51
     * @return Shipping
356 51
     */
357 51
    private function createShippingFromCustomer(Customer $Customer)
358 51
    {
359
        $Shipping = new Shipping();
360
        $Shipping
361
            ->setName01($Customer->getName01())
362 51
            ->setName02($Customer->getName02())
363
            ->setKana01($Customer->getKana01())
364 51
            ->setKana02($Customer->getKana02())
365 51
            ->setCompanyName($Customer->getCompanyName())
366 51
            ->setPhoneNumber($Customer->getPhoneNumber())
367 51
            ->setPostalCode($Customer->getPostalCode())
368 51
            ->setPref($Customer->getPref())
369
            ->setAddr01($Customer->getAddr01())
370
            ->setAddr02($Customer->getAddr02());
371
372
        return $Shipping;
373
    }
374
375
    /**
376
     * @param Shipping $Shipping
377
     */
378
    private function setDefaultDelivery(Shipping $Shipping)
379
    {
380
        // 配送商品に含まれる販売種別を抽出.
381
        $OrderItems = $Shipping->getOrderItems();
382
        $SaleTypes = [];
383
        /** @var OrderItem $OrderItem */
384 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
385
            $ProductClass = $OrderItem->getProductClass();
386
            $SaleType = $ProductClass->getSaleType();
387
            $SaleTypes[$SaleType->getId()] = $SaleType;
388
        }
389
390
        // 販売種別に紐づく配送業者を取得.
391
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
392
393
        // 初期の配送業者を設定
394
        $Delivery = current($Deliveries);
395
        $Shipping->setDelivery($Delivery);
396
        $Shipping->setShippingDeliveryName($Delivery->getName());
397
    }
398
399
    /**
400
     * @param Order $Order
401
     */
402
    private function setDefaultPayment(Order $Order)
403
    {
404
        $OrderItems = $Order->getOrderItems();
405
406
        // 受注明細に含まれる販売種別を抽出.
407
        $SaleTypes = [];
408
        /** @var OrderItem $OrderItem */
409 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
410
            $ProductClass = $OrderItem->getProductClass();
411
            if (is_null($ProductClass)) {
412
                // 商品明細のみ対象とする. 送料明細等はスキップする.
413
                continue;
414
            }
415
            $SaleType = $ProductClass->getSaleType();
416
            $SaleTypes[$SaleType->getId()] = $SaleType;
417
        }
418
419
        // 販売種別に紐づく配送業者を抽出
420
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
421
422
        // 利用可能な支払い方法を抽出.
423
        $Payments = $this->paymentRepository->findAllowedPayments($Deliveries, true);
424
425
        // 初期の支払い方法を設定.
426
        $Payment = current($Payments);
427
        if ($Payment) {
428
            $Order->setPayment($Payment);
429
            $Order->setPaymentMethod($Payment->getMethod());
430
        }
431
    }
432
433
    /**
434
     * @param Order $Order
435
     * @param Shipping $Shipping
436
     * @param array $OrderItems
437
     */
438
    private function addOrderItems(Order $Order, Shipping $Shipping, array $OrderItems)
439
    {
440
        foreach ($OrderItems as $OrderItem) {
441
            $Shipping->addOrderItem($OrderItem);
442
            $Order->addOrderItem($OrderItem);
443
            $OrderItem->setOrder($Order);
444
            $OrderItem->setShipping($Shipping);
445
        }
446
    }
447
}
448