Failed Conditions
Pull Request — 4.0 (#3650)
by chihiro
09:08 queued 02:38
created

OrderHelper::getPurchaseProcessingOrder()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 11
ccs 4
cts 4
cp 1
crap 2
rs 9.9
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\Common\Collections\ArrayCollection;
17
use Doctrine\ORM\EntityManagerInterface;
18
use Eccube\Entity\Cart;
19
use Eccube\Entity\CartItem;
20
use Eccube\Entity\Customer;
21
use Eccube\Entity\Master\DeviceType;
22
use Eccube\Entity\Master\OrderItemType;
23
use Eccube\Entity\Master\OrderStatus;
24
use Eccube\Entity\Order;
25
use Eccube\Entity\OrderItem;
26
use Eccube\Entity\Shipping;
27
use Eccube\EventListener\SecurityListener;
28
use Eccube\Repository\DeliveryRepository;
29
use Eccube\Repository\Master\DeviceTypeRepository;
30
use Eccube\Repository\Master\OrderItemTypeRepository;
31
use Eccube\Repository\Master\OrderStatusRepository;
32
use Eccube\Repository\Master\PrefRepository;
33
use Eccube\Repository\OrderRepository;
34
use Eccube\Repository\PaymentRepository;
35
use Eccube\Util\StringUtil;
36
use SunCat\MobileDetectBundle\DeviceDetector\MobileDetector;
37
use Symfony\Bundle\FrameworkBundle\Controller\ControllerTrait;
38
use Symfony\Component\DependencyInjection\ContainerInterface;
39
use Symfony\Component\HttpFoundation\Session\SessionInterface;
40
41
class OrderHelper
42
{
43
    // FIXME 必要なメソッドのみ移植する
44
    use ControllerTrait;
45
46
    /**
47
     * @var ContainerInterface
48
     */
49
    protected $container;
50
51
    /**
52
     * @var string 非会員情報を保持するセッションのキー
53
     */
54
    const SESSION_NON_MEMBER = 'eccube.front.shopping.nonmember';
55
56
    /**
57
     * @var string 非会員の住所情報を保持するセッションのキー
58
     */
59
    const SESSION_NON_MEMBER_ADDRESSES = 'eccube.front.shopping.nonmember.customeraddress';
60
61
    /**
62
     * @var string 受注IDを保持するセッションのキー
63
     */
64
    const SESSION_ORDER_ID = 'eccube.front.shopping.order.id';
65
66
    /**
67
     * @var string カートが分割されているかどうかのフラグ. 購入フローからのログイン時にカートが分割された場合にtrueがセットされる.
68
     *
69
     * @see SecurityListener
70
     */
71
    const SESSION_CART_DEVIDE_FLAG = 'eccube.front.cart.divide';
72
73
    /**
74
     * @var SessionInterface
75
     */
76
    protected $session;
77
78
    /**
79
     * @var PrefRepository
80
     */
81
    protected $prefRepository;
82
83
    /**
84
     * @var OrderRepository
85
     */
86
    protected $orderRepository;
87
88
    /**
89
     * @var OrderItemTypeRepository
90
     */
91
    protected $orderItemTypeRepository;
92
93
    public function __construct(
94
        ContainerInterface $container,
95
        EntityManagerInterface $entityManager,
96
        OrderRepository $orderRepository,
97
        OrderItemTypeRepository $orderItemTypeRepository,
98
        OrderStatusRepository $orderStatusRepository,
99
        DeliveryRepository $deliveryRepository,
100
        PaymentRepository $paymentRepository,
101
        DeviceTypeRepository $deviceTypeRepository,
102
        PrefRepository $prefRepository,
103
        MobileDetector $mobileDetector,
104
        SessionInterface $session
105
    ) {
106
        $this->container = $container;
107
        $this->orderRepository = $orderRepository;
108
        $this->orderStatusRepository = $orderStatusRepository;
109
        $this->orderItemTypeRepository = $orderItemTypeRepository;
110
        $this->deliveryRepository = $deliveryRepository;
111
        $this->paymentRepository = $paymentRepository;
112
        $this->deviceTypeRepository = $deviceTypeRepository;
113
        $this->entityManager = $entityManager;
114
        $this->prefRepository = $prefRepository;
115 175
        $this->mobileDetector = $mobileDetector;
116
        $this->session = $session;
117
    }
118
119
    /**
120
     * 購入処理中の受注を生成する.
121
     *
122
     * @param Customer $Customer
123
     * @param $CartItems
124
     *
125
     * @return Order
126
     */
127
    public function createPurchaseProcessingOrder(Cart $Cart, Customer $Customer)
128 175
    {
129 175
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
130 175
        $Order = new Order($OrderStatus);
131 175
132 175
        $preOrderId = $this->createPreOrderId();
133 175
        $Order->setPreOrderId($preOrderId);
134 175
135 175
        // 顧客情報の設定
136 175
        $this->setCustomer($Order, $Customer);
137 175
138 175
        $DeviceType = $this->deviceTypeRepository->find($this->mobileDetector->isMobile() ? DeviceType::DEVICE_TYPE_MB : DeviceType::DEVICE_TYPE_PC);
139
        $Order->setDeviceType($DeviceType);
140
141
        // 明細情報の設定
142
        $OrderItems = $this->createOrderItemsFromCartItems($Cart->getCartItems());
0 ignored issues
show
Bug introduced by
It seems like $Cart->getCartItems() targeting Eccube\Entity\Cart::getCartItems() can also be of type object<Doctrine\Common\Collections\Collection>; however, Eccube\Service\OrderHelp...derItemsFromCartItems() does only seem to accept object<Doctrine\Common\C...ccube\Entity\CartItem>>, 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...
143
        $OrderItemsGroupBySaleType = array_reduce($OrderItems, function ($result, $item) {
144
            /* @var OrderItem $item */
145
            $saleTypeId = $item->getProductClass()->getSaleType()->getId();
146
            $result[$saleTypeId][] = $item;
147
148
            return $result;
149 51
        }, []);
150
151 51
        foreach ($OrderItemsGroupBySaleType as $OrderItems) {
152 51
            $Shipping = $this->createShippingFromCustomer($Customer);
153
            $Shipping->setOrder($Order);
154 51
            $this->addOrderItems($Order, $Shipping, $OrderItems);
155
            $this->setDefaultDelivery($Shipping);
156 51
            $this->entityManager->persist($Shipping);
157
            $Order->addShipping($Shipping);
158
        }
159
160 51
        $this->setDefaultPayment($Order);
161
162 51
        $this->entityManager->persist($Order);
163 51
164
        return $Order;
165
    }
166 51
167 51
    /**
168
     * @param Cart $Cart
169 51
     *
170 51
     * @return bool
171
     */
172 51
    public function verifyCart(Cart $Cart)
173 51
    {
174
        if (count($Cart->getCartItems()) > 0) {
175 51
            $divide = $this->session->get(self::SESSION_CART_DEVIDE_FLAG);
176 51
            if ($divide) {
177 51
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
178 51
179 51
                return false;
180 51
            }
181 51
182
            return true;
183
        }
184 51
185
        log_info('カートに商品が入っていません。');
186 51
187
        return false;
188
    }
189 51
190
    /**
191
     * 注文手続き画面でログインが必要かどうかの判定
192
     *
193
     * @return bool
194
     */
195
    public function isLoginRequired()
196
    {
197
        // フォームログイン済はログイン不要
198
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
199 2
            return false;
200
        }
201 2
202 2
        // Remember Meログイン済の場合はフォームからのログインが必要
203
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
204 2
            return true;
205 1
        }
206 1
207 1
        // 未ログインだがお客様情報を入力している場合はログイン不要
208 1
        if (!$this->getUser() && $this->getNonMember()) {
209 1
            return false;
210 1
        }
211 1
212
        return true;
213
    }
214 2
215
    /**
216
     * 購入処理中の受注を取得する.
217 51
     *
218
     * @param null|string $preOrderId
219
     *
220
     * @return null|Order
221 51
     */
222
    public function getPurchaseProcessingOrder($preOrderId = null)
223 51
    {
224
        if (null === $preOrderId) {
225 51
            return null;
226
        }
227
228
        return $this->orderRepository->findOneBy([
229 51
            'pre_order_id' => $preOrderId,
230
            'OrderStatus' => OrderStatus::PROCESSING,
231 51
        ]);
232
    }
233
234 51
    /**
235
     * セッションに保持されている非会員情報を取得する.
236 51
     * 非会員購入時に入力されたお客様情報を返す.
237 34
     *
238
     * @return Customer
239
     */
240 51
    public function getNonMember()
241 51
    {
242
        $NonMember = $this->session->get(self::SESSION_NON_MEMBER);
243 51
        if ($NonMember && $NonMember->getPref()) {
244
            $Pref = $this->prefRepository->find($NonMember->getPref()->getId());
245
            $NonMember->setPref($Pref);
246
        }
247
248
        return $NonMember;
249
    }
250
251
    /**
252
     * @param Cart $Cart
253
     * @param Customer $Customer
254
     *
255
     * @return Order|null
256 51
     */
257
    public function initializeOrder(Cart $Cart, Customer $Customer)
258 51
    {
259
        // 購入処理中の受注情報を取得
260 51
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
261
            return $Order;
262
        }
263 51
264
        // 受注情報を作成
265 51
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
266
        $Cart->setPreOrderId($Order->getPreOrderId());
267 51
268
        return $Order;
269 51
    }
270 51
271 51
    public function removeSession()
272 51
    {
273 51
        $this->session->remove(self::SESSION_ORDER_ID);
274 51
        $this->session->remove(self::SESSION_ORDER_ID);
275 51
        $this->session->remove(self::SESSION_NON_MEMBER);
276
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
277 51
    }
278 51
279 51
    private function createPreOrderId()
280 51
    {
281
        // ランダムなpre_order_idを作成
282 51
        do {
283 51
            $preOrderId = sha1(StringUtil::random(32));
284 42
285 42
            $Order = $this->orderRepository->findOneBy(
286
                [
287
                    'pre_order_id' => $preOrderId,
288 51
                    'OrderStatus' => OrderStatus::PROCESSING,
289 51
                ]
290
            );
291
        } while ($Order);
292 51
293
        return $preOrderId;
294 51
    }
295
296 51
    private function setCustomer(Order $Order, Customer $Customer)
297 51
    {
298 51
        if ($Customer->getId()) {
299 51
            $Order->setCustomer($Customer);
300 51
        }
301 51
302 51
        $Order->copyProperties(
303 51
            $Customer,
304 51
            [
305 51
                'id',
306
                'create_date',
307 51
                'update_date',
308
                'del_flg',
309
            ]
310 51
        );
311
    }
312
313 51
    /**
314 51
     * @param ArrayCollection|CartItem[] $CartItems
315
     *
316 51
     * @return OrderItem[]
317 51
     */
318 51
    private function createOrderItemsFromCartItems($CartItems)
319 51
    {
320
        $ProductItemType = $this->orderItemTypeRepository->find(OrderItemType::PRODUCT);
321
322
        return array_map(function ($item) use ($ProductItemType) {
323 51
            /* @var $item CartItem */
324
            /* @var $ProductClass \Eccube\Entity\ProductClass */
325
            $ProductClass = $item->getProductClass();
326 51
            /* @var $Product \Eccube\Entity\Product */
327 51
            $Product = $ProductClass->getProduct();
328 51
329
            $OrderItem = new OrderItem();
330
            $OrderItem
331 51
                ->setProduct($Product)
332
                ->setProductClass($ProductClass)
333 51
                ->setProductName($Product->getName())
334
                ->setProductCode($ProductClass->getCode())
335
                ->setPrice($ProductClass->getPrice02())
336 51
                ->setQuantity($item->getQuantity())
337
                ->setOrderItemType($ProductItemType);
0 ignored issues
show
Bug introduced by
It seems like $ProductItemType defined by $this->orderItemTypeRepo...OrderItemType::PRODUCT) on line 320 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...
338 51
339 51
            $ClassCategory1 = $ProductClass->getClassCategory1();
340 51
            if (!is_null($ClassCategory1)) {
341
                $OrderItem->setClasscategoryName1($ClassCategory1->getName());
342
                $OrderItem->setClassName1($ClassCategory1->getClassName()->getName());
343
            }
344 51
            $ClassCategory2 = $ProductClass->getClassCategory2();
345 51
            if (!is_null($ClassCategory2)) {
346
                $OrderItem->setClasscategoryName2($ClassCategory2->getName());
347
                $OrderItem->setClassName2($ClassCategory2->getClassName()->getName());
348
            }
349 51
350
            return $OrderItem;
351
        }, $CartItems->toArray());
0 ignored issues
show
Bug introduced by
It seems like $CartItems is not always an object, but can also be of type array<integer,object<Eccube\Entity\CartItem>>. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

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