Failed Conditions
Pull Request — 4.0 (#3650)
by chihiro
08:35
created

OrderHelper::createShippingFromCustomer()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 17
ccs 9
cts 9
cp 1
crap 1
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
     *
123
     * @return Order
124
     */
125
    public function createPurchaseProcessingOrder(Cart $Cart, Customer $Customer)
126
    {
127
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
128 175
        $Order = new Order($OrderStatus);
129 175
130 175
        $preOrderId = $this->createPreOrderId();
131 175
        $Order->setPreOrderId($preOrderId);
132 175
133 175
        // 顧客情報の設定
134 175
        $this->setCustomer($Order, $Customer);
135 175
136 175
        $DeviceType = $this->deviceTypeRepository->find($this->mobileDetector->isMobile() ? DeviceType::DEVICE_TYPE_MB : DeviceType::DEVICE_TYPE_PC);
137 175
        $Order->setDeviceType($DeviceType);
138 175
139
        // 明細情報の設定
140
        $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...
141
        $OrderItemsGroupBySaleType = array_reduce($OrderItems, function ($result, $item) {
142
            /* @var OrderItem $item */
143
            $saleTypeId = $item->getProductClass()->getSaleType()->getId();
144
            $result[$saleTypeId][] = $item;
145
146
            return $result;
147
        }, []);
148
149 51
        foreach ($OrderItemsGroupBySaleType as $OrderItems) {
150
            $Shipping = $this->createShippingFromCustomer($Customer);
151 51
            $Shipping->setOrder($Order);
152 51
            $this->addOrderItems($Order, $Shipping, $OrderItems);
153
            $this->setDefaultDelivery($Shipping);
154 51
            $this->entityManager->persist($Shipping);
155
            $Order->addShipping($Shipping);
156 51
        }
157
158
        $this->setDefaultPayment($Order);
159
160 51
        $this->entityManager->persist($Order);
161
162 51
        return $Order;
163 51
    }
164
165
    /**
166 51
     * @param Cart|null $Cart
167 51
     * @return bool
168
     */
169 51
    public function verifyCart(Cart $Cart = null)
170 51
    {
171
        if ($Cart && count($Cart->getCartItems()) > 0) {
172 51
            $divide = $this->session->get(self::SESSION_CART_DEVIDE_FLAG);
173 51
            if ($divide) {
174
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
175 51
176 51
                return false;
177 51
            }
178 51
179 51
            return true;
180 51
        }
181 51
182
        log_info('カートに商品が入っていません。');
183
184 51
        return false;
185
    }
186 51
187
    /**
188
     * 注文手続き画面でログインが必要かどうかの判定
189 51
     *
190
     * @return bool
191
     */
192
    public function isLoginRequired()
193
    {
194
        // フォームログイン済はログイン不要
195
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
196
            return false;
197
        }
198
199 2
        // Remember Meログイン済の場合はフォームからのログインが必要
200
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
201 2
            return true;
202 2
        }
203
204 2
        // 未ログインだがお客様情報を入力している場合はログイン不要
205 1
        if (!$this->getUser() && $this->getNonMember()) {
206 1
            return false;
207 1
        }
208 1
209 1
        return true;
210 1
    }
211 1
212
    /**
213
     * 購入処理中の受注を取得する.
214 2
     *
215
     * @param null|string $preOrderId
216
     *
217 51
     * @return null|Order
218
     */
219
    public function getPurchaseProcessingOrder($preOrderId = null)
220
    {
221 51
        if (!$preOrderId) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $preOrderId of type null|string is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
222
            return null;
223 51
        }
224
225 51
        return $this->orderRepository->findOneBy([
226
            'pre_order_id' => $preOrderId,
227
            'OrderStatus' => OrderStatus::PROCESSING,
228
        ]);
229 51
    }
230
231 51
    /**
232
     * セッションに保持されている非会員情報を取得する.
233
     * 非会員購入時に入力されたお客様情報を返す.
234 51
     *
235
     * @return Customer
236 51
     */
237 34 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...
238
    {
239
        $NonMember = $this->session->get(self::SESSION_NON_MEMBER);
240 51
        if ($NonMember && $NonMember->getPref()) {
241 51
            $Pref = $this->prefRepository->find($NonMember->getPref()->getId());
242
            $NonMember->setPref($Pref);
243 51
        }
244
245
        return $NonMember;
246
    }
247
248
    /**
249
     * @param Cart $Cart
250
     * @param Customer $Customer
251
     *
252
     * @return Order|null
253
     */
254
    public function initializeOrder(Cart $Cart, Customer $Customer)
255
    {
256 51
        // 購入処理中の受注情報を取得
257
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
258 51
            return $Order;
259
        }
260 51
261
        // 受注情報を作成
262
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
263 51
        $Cart->setPreOrderId($Order->getPreOrderId());
264
265 51
        return $Order;
266
    }
267 51
268
    public function removeSession()
269 51
    {
270 51
        $this->session->remove(self::SESSION_ORDER_ID);
271 51
        $this->session->remove(self::SESSION_ORDER_ID);
272 51
        $this->session->remove(self::SESSION_NON_MEMBER);
273 51
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
274 51
    }
275 51
276
    private function createPreOrderId()
277 51
    {
278 51
        // ランダムなpre_order_idを作成
279 51
        do {
280 51
            $preOrderId = sha1(StringUtil::random(32));
281
282 51
            $Order = $this->orderRepository->findOneBy(
283 51
                [
284 42
                    'pre_order_id' => $preOrderId,
285 42
                    'OrderStatus' => OrderStatus::PROCESSING,
286
                ]
287
            );
288 51
        } while ($Order);
289 51
290
        return $preOrderId;
291
    }
292 51
293
    private function setCustomer(Order $Order, Customer $Customer)
294 51
    {
295
        if ($Customer->getId()) {
296 51
            $Order->setCustomer($Customer);
297 51
        }
298 51
299 51
        $Order->copyProperties(
300 51
            $Customer,
301 51
            [
302 51
                'id',
303 51
                'create_date',
304 51
                'update_date',
305 51
                'del_flg',
306
            ]
307 51
        );
308
    }
309
310 51
    /**
311
     * @param ArrayCollection $CartItems
312
     *
313 51
     * @return OrderItem[]
314 51
     */
315
    private function createOrderItemsFromCartItems($CartItems)
316 51
    {
317 51
        $ProductItemType = $this->orderItemTypeRepository->find(OrderItemType::PRODUCT);
318 51
319 51
        return array_map(function ($item) use ($ProductItemType) {
320
            /* @var $item CartItem */
321
            /* @var $ProductClass \Eccube\Entity\ProductClass */
322
            $ProductClass = $item->getProductClass();
323 51
            /* @var $Product \Eccube\Entity\Product */
324
            $Product = $ProductClass->getProduct();
325
326 51
            $OrderItem = new OrderItem();
327 51
            $OrderItem
328 51
                ->setProduct($Product)
329
                ->setProductClass($ProductClass)
330
                ->setProductName($Product->getName())
331 51
                ->setProductCode($ProductClass->getCode())
332
                ->setPrice($ProductClass->getPrice02())
333 51
                ->setQuantity($item->getQuantity())
334
                ->setOrderItemType($ProductItemType);
0 ignored issues
show
Bug introduced by
It seems like $ProductItemType defined by $this->orderItemTypeRepo...OrderItemType::PRODUCT) on line 317 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...
335
336 51
            $ClassCategory1 = $ProductClass->getClassCategory1();
337
            if (!is_null($ClassCategory1)) {
338 51
                $OrderItem->setClasscategoryName1($ClassCategory1->getName());
339 51
                $OrderItem->setClassName1($ClassCategory1->getClassName()->getName());
340 51
            }
341
            $ClassCategory2 = $ProductClass->getClassCategory2();
342
            if (!is_null($ClassCategory2)) {
343
                $OrderItem->setClasscategoryName2($ClassCategory2->getName());
344 51
                $OrderItem->setClassName2($ClassCategory2->getClassName()->getName());
345 51
            }
346
347
            return $OrderItem;
348
        }, $CartItems->toArray());
349 51
    }
350
351
    /**
352 51
     * @param Customer $Customer
353
     *
354
     * @return Shipping
355 51
     */
356 51
    private function createShippingFromCustomer(Customer $Customer)
357 51
    {
358 51
        $Shipping = new Shipping();
359
        $Shipping
360
            ->setName01($Customer->getName01())
361
            ->setName02($Customer->getName02())
362 51
            ->setKana01($Customer->getKana01())
363
            ->setKana02($Customer->getKana02())
364 51
            ->setCompanyName($Customer->getCompanyName())
365 51
            ->setPhoneNumber($Customer->getPhoneNumber())
366 51
            ->setPostalCode($Customer->getPostalCode())
367 51
            ->setPref($Customer->getPref())
368 51
            ->setAddr01($Customer->getAddr01())
369
            ->setAddr02($Customer->getAddr02());
370
371
        return $Shipping;
372
    }
373
374
    /**
375
     * @param Shipping $Shipping
376
     */
377
    private function setDefaultDelivery(Shipping $Shipping)
378
    {
379
        // 配送商品に含まれる販売種別を抽出.
380
        $OrderItems = $Shipping->getOrderItems();
381
        $SaleTypes = [];
382
        /** @var OrderItem $OrderItem */
383 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
384
            $ProductClass = $OrderItem->getProductClass();
385
            $SaleType = $ProductClass->getSaleType();
386
            $SaleTypes[$SaleType->getId()] = $SaleType;
387
        }
388
389
        // 販売種別に紐づく配送業者を取得.
390
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
391
392
        // 初期の配送業者を設定
393
        $Delivery = current($Deliveries);
394
        $Shipping->setDelivery($Delivery);
395
        $Shipping->setShippingDeliveryName($Delivery->getName());
396
    }
397
398
    /**
399
     * @param Order $Order
400
     */
401
    private function setDefaultPayment(Order $Order)
402
    {
403
        $OrderItems = $Order->getOrderItems();
404
405
        // 受注明細に含まれる販売種別を抽出.
406
        $SaleTypes = [];
407
        /** @var OrderItem $OrderItem */
408 View Code Duplication
        foreach ($OrderItems as $OrderItem) {
409
            $ProductClass = $OrderItem->getProductClass();
410
            if (is_null($ProductClass)) {
411
                // 商品明細のみ対象とする. 送料明細等はスキップする.
412
                continue;
413
            }
414
            $SaleType = $ProductClass->getSaleType();
415
            $SaleTypes[$SaleType->getId()] = $SaleType;
416
        }
417
418
        // 販売種別に紐づく配送業者を抽出
419
        $Deliveries = $this->deliveryRepository->getDeliveries($SaleTypes);
420
421
        // 利用可能な支払い方法を抽出.
422
        $Payments = $this->paymentRepository->findAllowedPayments($Deliveries, true);
423
424
        // 初期の支払い方法を設定.
425
        $Payment = current($Payments);
426
        if ($Payment) {
427
            $Order->setPayment($Payment);
428
            $Order->setPaymentMethod($Payment->getMethod());
429
        }
430
    }
431
432
    /**
433
     * @param Order $Order
434
     * @param Shipping $Shipping
435
     * @param array $OrderItems
436
     */
437
    private function addOrderItems(Order $Order, Shipping $Shipping, array $OrderItems)
438
    {
439
        foreach ($OrderItems as $OrderItem) {
440
            $Shipping->addOrderItem($OrderItem);
441
            $Order->addOrderItem($OrderItem);
442
            $OrderItem->setOrder($Order);
443
            $OrderItem->setShipping($Shipping);
444
        }
445
    }
446
}
447