Completed
Push — dev/recommend-plugins ( b4eeb7...acd8c2 )
by Kiyotaka
08:44
created

OrderHelper   A

Complexity

Total Complexity 39

Size/Duplication

Total Lines 422
Duplicated Lines 3.32 %

Coupling/Cohesion

Components 2
Dependencies 15

Importance

Changes 0
Metric Value
dl 14
loc 422
rs 9.28
c 0
b 0
f 0
wmc 39
lcom 2
cbo 15

16 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 25 1
A createPurchaseProcessingOrder() 0 39 3
A verifyCart() 0 17 3
A isLoginRequired() 0 19 5
A getPurchaseProcessingOrder() 0 11 2
A getNonMember() 0 10 3
A initializeOrder() 0 13 2
A removeSession() 0 7 1
A updateCustomerInfo() 0 6 2
A createPreOrderId() 0 16 2
A setCustomer() 0 16 2
A createOrderItemsFromCartItems() 0 35 4
A createShippingFromCustomer() 0 17 1
A setDefaultDelivery() 5 20 2
A setDefaultPayment() 9 30 4
A addOrderItems() 0 9 2

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

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\Common\Collections\ArrayCollection;
17
use Doctrine\Common\Collections\Collection;
18
use Doctrine\ORM\EntityManagerInterface;
19
use Eccube\Entity\Cart;
20
use Eccube\Entity\CartItem;
21
use Eccube\Entity\Customer;
22
use Eccube\Entity\Master\DeviceType;
23
use Eccube\Entity\Master\OrderItemType;
24
use Eccube\Entity\Master\OrderStatus;
25
use Eccube\Entity\Order;
26
use Eccube\Entity\OrderItem;
27
use Eccube\Entity\Shipping;
28
use Eccube\EventListener\SecurityListener;
29
use Eccube\Repository\DeliveryRepository;
30
use Eccube\Repository\Master\DeviceTypeRepository;
31
use Eccube\Repository\Master\OrderItemTypeRepository;
32
use Eccube\Repository\Master\OrderStatusRepository;
33
use Eccube\Repository\Master\PrefRepository;
34
use Eccube\Repository\OrderRepository;
35
use Eccube\Repository\PaymentRepository;
36
use Eccube\Util\StringUtil;
37
use SunCat\MobileDetectBundle\DeviceDetector\MobileDetector;
38
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
39
use Symfony\Component\DependencyInjection\ContainerInterface;
40
use Symfony\Component\HttpFoundation\Session\SessionInterface;
41
42
class OrderHelper
43
{
44
    // FIXME 必要なメソッドのみ移植する
45
    use ControllerTrait;
46
47
    /**
48
     * @var ContainerInterface
49
     */
50
    protected $container;
51
52
    /**
53
     * @var string 非会員情報を保持するセッションのキー
54
     */
55
    const SESSION_NON_MEMBER = 'eccube.front.shopping.nonmember';
56
57
    /**
58
     * @var string 非会員の住所情報を保持するセッションのキー
59
     */
60
    const SESSION_NON_MEMBER_ADDRESSES = 'eccube.front.shopping.nonmember.customeraddress';
61
62
    /**
63
     * @var string 受注IDを保持するセッションのキー
64
     */
65
    const SESSION_ORDER_ID = 'eccube.front.shopping.order.id';
66
67
    /**
68
     * @var string カートが分割されているかどうかのフラグ. 購入フローからのログイン時にカートが分割された場合にtrueがセットされる.
69
     *
70
     * @see SecurityListener
71
     */
72
    const SESSION_CART_DEVIDE_FLAG = 'eccube.front.cart.divide';
73
74
    /**
75
     * @var SessionInterface
76
     */
77
    protected $session;
78
79
    /**
80
     * @var PrefRepository
81
     */
82
    protected $prefRepository;
83
84
    /**
85
     * @var OrderRepository
86
     */
87
    protected $orderRepository;
88
89
    /**
90
     * @var OrderItemTypeRepository
91
     */
92
    protected $orderItemTypeRepository;
93
94
    public function __construct(
95
        ContainerInterface $container,
96
        EntityManagerInterface $entityManager,
97
        OrderRepository $orderRepository,
98
        OrderItemTypeRepository $orderItemTypeRepository,
99
        OrderStatusRepository $orderStatusRepository,
100
        DeliveryRepository $deliveryRepository,
101
        PaymentRepository $paymentRepository,
102
        DeviceTypeRepository $deviceTypeRepository,
103
        PrefRepository $prefRepository,
104
        MobileDetector $mobileDetector,
105
        SessionInterface $session
106
    ) {
107
        $this->container = $container;
108
        $this->orderRepository = $orderRepository;
109
        $this->orderStatusRepository = $orderStatusRepository;
0 ignored issues
show
Bug introduced by
The property orderStatusRepository does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
110
        $this->orderItemTypeRepository = $orderItemTypeRepository;
111
        $this->deliveryRepository = $deliveryRepository;
0 ignored issues
show
Bug introduced by
The property deliveryRepository does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
112
        $this->paymentRepository = $paymentRepository;
0 ignored issues
show
Bug introduced by
The property paymentRepository does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
113
        $this->deviceTypeRepository = $deviceTypeRepository;
0 ignored issues
show
Bug introduced by
The property deviceTypeRepository does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
114
        $this->entityManager = $entityManager;
0 ignored issues
show
Bug introduced by
The property entityManager does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
115
        $this->prefRepository = $prefRepository;
116
        $this->mobileDetector = $mobileDetector;
0 ignored issues
show
Bug introduced by
The property mobileDetector does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
117
        $this->session = $session;
118
    }
119
120
    /**
121
     * 購入処理中の受注を生成する.
122
     *
123
     * @param Customer $Customer
124
     * @param $CartItems
125
     *
126
     * @return Order
127
     */
128
    public function createPurchaseProcessingOrder(Cart $Cart, Customer $Customer)
129
    {
130
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
131
        $Order = new Order($OrderStatus);
132
133
        $preOrderId = $this->createPreOrderId();
134
        $Order->setPreOrderId($preOrderId);
135
136
        // 顧客情報の設定
137
        $this->setCustomer($Order, $Customer);
138
139
        $DeviceType = $this->deviceTypeRepository->find($this->mobileDetector->isMobile() ? DeviceType::DEVICE_TYPE_MB : DeviceType::DEVICE_TYPE_PC);
140
        $Order->setDeviceType($DeviceType);
141
142
        // 明細情報の設定
143
        $OrderItems = $this->createOrderItemsFromCartItems($Cart->getCartItems());
144
        $OrderItemsGroupBySaleType = array_reduce($OrderItems, function ($result, $item) {
145
            /* @var OrderItem $item */
146
            $saleTypeId = $item->getProductClass()->getSaleType()->getId();
147
            $result[$saleTypeId][] = $item;
148
149
            return $result;
150
        }, []);
151
152
        foreach ($OrderItemsGroupBySaleType as $OrderItems) {
153
            $Shipping = $this->createShippingFromCustomer($Customer);
154
            $Shipping->setOrder($Order);
155
            $this->addOrderItems($Order, $Shipping, $OrderItems);
156
            $this->setDefaultDelivery($Shipping);
157
            $this->entityManager->persist($Shipping);
158
            $Order->addShipping($Shipping);
159
        }
160
161
        $this->setDefaultPayment($Order);
162
163
        $this->entityManager->persist($Order);
164
165
        return $Order;
166
    }
167
168
    /**
169
     * @param Cart $Cart
170
     *
171
     * @return bool
172
     */
173
    public function verifyCart(Cart $Cart)
174
    {
175
        if (count($Cart->getCartItems()) > 0) {
176
            $divide = $this->session->get(self::SESSION_CART_DEVIDE_FLAG);
177
            if ($divide) {
178
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
179
180
                return false;
181
            }
182
183
            return true;
184
        }
185
186
        log_info('カートに商品が入っていません。');
187
188
        return false;
189
    }
190
191
    /**
192
     * 注文手続き画面でログインが必要かどうかの判定
193
     *
194
     * @return bool
195
     */
196
    public function isLoginRequired()
197
    {
198
        // フォームログイン済はログイン不要
199
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
200
            return false;
201
        }
202
203
        // Remember Meログイン済の場合はフォームからのログインが必要
204
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
205
            return true;
206
        }
207
208
        // 未ログインだがお客様情報を入力している場合はログイン不要
209
        if (!$this->getUser() && $this->getNonMember()) {
210
            return false;
211
        }
212
213
        return true;
214
    }
215
216
    /**
217
     * 購入処理中の受注を取得する.
218
     *
219
     * @param null|string $preOrderId
220
     *
221
     * @return null|Order
222
     */
223
    public function getPurchaseProcessingOrder($preOrderId = null)
224
    {
225
        if (null === $preOrderId) {
226
            return null;
227
        }
228
229
        return $this->orderRepository->findOneBy([
230
            'pre_order_id' => $preOrderId,
231
            'OrderStatus' => OrderStatus::PROCESSING,
232
        ]);
233
    }
234
235
    /**
236
     * セッションに保持されている非会員情報を取得する.
237
     * 非会員購入時に入力されたお客様情報を返す.
238
     *
239
     * @return Customer
240
     */
241
    public function getNonMember()
242
    {
243
        $NonMember = $this->session->get(self::SESSION_NON_MEMBER);
244
        if ($NonMember && $NonMember->getPref()) {
245
            $Pref = $this->prefRepository->find($NonMember->getPref()->getId());
246
            $NonMember->setPref($Pref);
247
        }
248
249
        return $NonMember;
250
    }
251
252
    /**
253
     * @param Cart $Cart
254
     * @param Customer $Customer
255
     *
256
     * @return Order|null
257
     */
258
    public function initializeOrder(Cart $Cart, Customer $Customer)
259
    {
260
        // 購入処理中の受注情報を取得
261
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
262
            return $Order;
263
        }
264
265
        // 受注情報を作成
266
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
267
        $Cart->setPreOrderId($Order->getPreOrderId());
268
269
        return $Order;
270
    }
271
272
    public function removeSession()
273
    {
274
        $this->session->remove(self::SESSION_ORDER_ID);
275
        $this->session->remove(self::SESSION_ORDER_ID);
276
        $this->session->remove(self::SESSION_NON_MEMBER);
277
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
278
    }
279
280
    /**
281
     * 会員情報の更新日時が受注の作成日時よりも新しければ, 受注の注文者情報を更新する.
282
     *
283
     * @param Order $Order
284
     * @param Customer $Customer
285
     */
286
    public function updateCustomerInfo(Order $Order, Customer $Customer)
287
    {
288
        if ($Order->getCreateDate() < $Customer->getUpdateDate()) {
289
            $this->setCustomer($Order, $Customer);
290
        }
291
    }
292
293
    private function createPreOrderId()
294
    {
295
        // ランダムなpre_order_idを作成
296
        do {
297
            $preOrderId = sha1(StringUtil::random(32));
298
299
            $Order = $this->orderRepository->findOneBy(
300
                [
301
                    'pre_order_id' => $preOrderId,
302
                    'OrderStatus' => OrderStatus::PROCESSING,
303
                ]
304
            );
305
        } while ($Order);
306
307
        return $preOrderId;
308
    }
309
310
    private function setCustomer(Order $Order, Customer $Customer)
311
    {
312
        if ($Customer->getId()) {
313
            $Order->setCustomer($Customer);
314
        }
315
316
        $Order->copyProperties(
317
            $Customer,
318
            [
319
                'id',
320
                'create_date',
321
                'update_date',
322
                'del_flg',
323
            ]
324
        );
325
    }
326
327
    /**
328
     * @param Collection|ArrayCollection|CartItem[] $CartItems
329
     *
330
     * @return OrderItem[]
331
     */
332
    private function createOrderItemsFromCartItems($CartItems)
333
    {
334
        $ProductItemType = $this->orderItemTypeRepository->find(OrderItemType::PRODUCT);
335
336
        return array_map(function ($item) use ($ProductItemType) {
337
            /* @var $item CartItem */
338
            /* @var $ProductClass \Eccube\Entity\ProductClass */
339
            $ProductClass = $item->getProductClass();
340
            /* @var $Product \Eccube\Entity\Product */
341
            $Product = $ProductClass->getProduct();
342
343
            $OrderItem = new OrderItem();
344
            $OrderItem
345
                ->setProduct($Product)
346
                ->setProductClass($ProductClass)
347
                ->setProductName($Product->getName())
348
                ->setProductCode($ProductClass->getCode())
349
                ->setPrice($ProductClass->getPrice02())
350
                ->setQuantity($item->getQuantity())
351
                ->setOrderItemType($ProductItemType);
0 ignored issues
show
Bug introduced by
It seems like $ProductItemType defined by $this->orderItemTypeRepo...OrderItemType::PRODUCT) on line 334 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...
352
353
            $ClassCategory1 = $ProductClass->getClassCategory1();
354
            if (!is_null($ClassCategory1)) {
355
                $OrderItem->setClasscategoryName1($ClassCategory1->getName());
356
                $OrderItem->setClassName1($ClassCategory1->getClassName()->getName());
357
            }
358
            $ClassCategory2 = $ProductClass->getClassCategory2();
359
            if (!is_null($ClassCategory2)) {
360
                $OrderItem->setClasscategoryName2($ClassCategory2->getName());
361
                $OrderItem->setClassName2($ClassCategory2->getClassName()->getName());
362
            }
363
364
            return $OrderItem;
365
        }, $CartItems instanceof Collection ? $CartItems->toArray() : $CartItems);
366
    }
367
368
    /**
369
     * @param Customer $Customer
370
     *
371
     * @return Shipping
372
     */
373
    private function createShippingFromCustomer(Customer $Customer)
374
    {
375
        $Shipping = new Shipping();
376
        $Shipping
377
            ->setName01($Customer->getName01())
378
            ->setName02($Customer->getName02())
379
            ->setKana01($Customer->getKana01())
380
            ->setKana02($Customer->getKana02())
381
            ->setCompanyName($Customer->getCompanyName())
382
            ->setPhoneNumber($Customer->getPhoneNumber())
383
            ->setPostalCode($Customer->getPostalCode())
384
            ->setPref($Customer->getPref())
385
            ->setAddr01($Customer->getAddr01())
386
            ->setAddr02($Customer->getAddr02());
387
388
        return $Shipping;
389
    }
390
391
    /**
392
     * @param Shipping $Shipping
393
     */
394
    private function setDefaultDelivery(Shipping $Shipping)
395
    {
396
        // 配送商品に含まれる販売種別を抽出.
397
        $OrderItems = $Shipping->getOrderItems();
398
        $SaleTypes = [];
399
        /** @var OrderItem $OrderItem */
400 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
401
            $ProductClass = $OrderItem->getProductClass();
402
            $SaleType = $ProductClass->getSaleType();
403
            $SaleTypes[$SaleType->getId()] = $SaleType;
404
        }
405
406
        // 販売種別に紐づく配送業者を取得.
407
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
408
409
        // 初期の配送業者を設定
410
        $Delivery = current($Deliveries);
411
        $Shipping->setDelivery($Delivery);
412
        $Shipping->setShippingDeliveryName($Delivery->getName());
413
    }
414
415
    /**
416
     * @param Order $Order
417
     */
418
    private function setDefaultPayment(Order $Order)
419
    {
420
        $OrderItems = $Order->getOrderItems();
421
422
        // 受注明細に含まれる販売種別を抽出.
423
        $SaleTypes = [];
424
        /** @var OrderItem $OrderItem */
425 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
426
            $ProductClass = $OrderItem->getProductClass();
427
            if (is_null($ProductClass)) {
428
                // 商品明細のみ対象とする. 送料明細等はスキップする.
429
                continue;
430
            }
431
            $SaleType = $ProductClass->getSaleType();
432
            $SaleTypes[$SaleType->getId()] = $SaleType;
433
        }
434
435
        // 販売種別に紐づく配送業者を抽出
436
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
437
438
        // 利用可能な支払い方法を抽出.
439
        $Payments = $this->paymentRepository->findAllowedPayments($Deliveries, true);
440
441
        // 初期の支払い方法を設定.
442
        $Payment = current($Payments);
443
        if ($Payment) {
444
            $Order->setPayment($Payment);
445
            $Order->setPaymentMethod($Payment->getMethod());
446
        }
447
    }
448
449
    /**
450
     * @param Order $Order
451
     * @param Shipping $Shipping
452
     * @param array $OrderItems
453
     */
454
    private function addOrderItems(Order $Order, Shipping $Shipping, array $OrderItems)
455
    {
456
        foreach ($OrderItems as $OrderItem) {
457
            $Shipping->addOrderItem($OrderItem);
458
            $Order->addOrderItem($OrderItem);
459
            $OrderItem->setOrder($Order);
460
            $OrderItem->setShipping($Shipping);
461
        }
462
    }
463
}
464