Completed
Pull Request — 4.0 (#3767)
by chihiro
23:02
created

OrderHelper::initializeOrder()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 0
loc 13
ccs 7
cts 7
cp 1
crap 2
rs 9.8333
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\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;
110
        $this->orderItemTypeRepository = $orderItemTypeRepository;
111
        $this->deliveryRepository = $deliveryRepository;
112
        $this->paymentRepository = $paymentRepository;
113
        $this->deviceTypeRepository = $deviceTypeRepository;
114
        $this->entityManager = $entityManager;
115 175
        $this->prefRepository = $prefRepository;
116
        $this->mobileDetector = $mobileDetector;
117
        $this->session = $session;
118
    }
119
120
    /**
121
     * 購入処理中の受注を生成する.
122
     *
123
     * @param Customer $Customer
124
     * @param $CartItems
125
     *
126
     * @return Order
127
     */
128 175
    public function createPurchaseProcessingOrder(Cart $Cart, Customer $Customer)
129 175
    {
130 175
        $OrderStatus = $this->orderStatusRepository->find(OrderStatus::PROCESSING);
131 175
        $Order = new Order($OrderStatus);
132 175
133 175
        $preOrderId = $this->createPreOrderId();
134 175
        $Order->setPreOrderId($preOrderId);
135 175
136 175
        // 顧客情報の設定
137 175
        $this->setCustomer($Order, $Customer);
138 175
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 51
            return $result;
150
        }, []);
151 51
152 51
        foreach ($OrderItemsGroupBySaleType as $OrderItems) {
153
            $Shipping = $this->createShippingFromCustomer($Customer);
154 51
            $Shipping->setOrder($Order);
155
            $this->addOrderItems($Order, $Shipping, $OrderItems);
156 51
            $this->setDefaultDelivery($Shipping);
157
            $this->entityManager->persist($Shipping);
158
            $Order->addShipping($Shipping);
159
        }
160 51
161
        $this->setDefaultPayment($Order);
162 51
163 51
        $this->entityManager->persist($Order);
164
165
        return $Order;
166 51
    }
167 51
168
    /**
169 51
     * @param Cart $Cart
170 51
     *
171
     * @return bool
172 51
     */
173 51
    public function verifyCart(Cart $Cart)
174
    {
175 51
        if (count($Cart->getCartItems()) > 0) {
176 51
            $divide = $this->session->get(self::SESSION_CART_DEVIDE_FLAG);
177 51
            if ($divide) {
178 51
                log_info('ログイン時に販売種別が異なる商品がカートと結合されました。');
179 51
180 51
                return false;
181 51
            }
182
183
            return true;
184 51
        }
185
186 51
        log_info('カートに商品が入っていません。');
187
188
        return false;
189 51
    }
190
191
    /**
192
     * 注文手続き画面でログインが必要かどうかの判定
193
     *
194
     * @return bool
195
     */
196
    public function isLoginRequired()
197
    {
198
        // フォームログイン済はログイン不要
199 2
        if ($this->isGranted('IS_AUTHENTICATED_FULLY')) {
200
            return false;
201 2
        }
202 2
203
        // Remember Meログイン済の場合はフォームからのログインが必要
204 2
        if ($this->isGranted('IS_AUTHENTICATED_REMEMBERED')) {
205 1
            return true;
206 1
        }
207 1
208 1
        // 未ログインだがお客様情報を入力している場合はログイン不要
209 1
        if (!$this->getUser() && $this->getNonMember()) {
210 1
            return false;
211 1
        }
212
213
        return true;
214 2
    }
215
216
    /**
217 51
     * 購入処理中の受注を取得する.
218
     *
219
     * @param null|string $preOrderId
220
     *
221 51
     * @return null|Order
222
     */
223 51
    public function getPurchaseProcessingOrder($preOrderId = null)
224
    {
225 51
        if (null === $preOrderId) {
226
            return null;
227
        }
228
229 51
        return $this->orderRepository->findOneBy([
230
            'pre_order_id' => $preOrderId,
231 51
            'OrderStatus' => OrderStatus::PROCESSING,
232
        ]);
233
    }
234 51
235
    /**
236 51
     * セッションに保持されている非会員情報を取得する.
237 34
     * 非会員購入時に入力されたお客様情報を返す.
238
     *
239
     * @return Customer
240 51
     */
241 51
    public function getNonMember()
242
    {
243 51
        $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 51
     * @return Order|null
257
     */
258 51
    public function initializeOrder(Cart $Cart, Customer $Customer)
259
    {
260 51
        // 購入処理中の受注情報を取得
261
        if ($Order = $this->getPurchaseProcessingOrder($Cart->getPreOrderId())) {
262
            return $Order;
263 51
        }
264
265 51
        // 受注情報を作成
266
        $Order = $this->createPurchaseProcessingOrder($Cart, $Customer);
267 51
        $Cart->setPreOrderId($Order->getPreOrderId());
268
269 51
        return $Order;
270 51
    }
271 51
272 51
    public function removeSession()
273 51
    {
274 51
        $this->session->remove(self::SESSION_ORDER_ID);
275 51
        $this->session->remove(self::SESSION_ORDER_ID);
276
        $this->session->remove(self::SESSION_NON_MEMBER);
277 51
        $this->session->remove(self::SESSION_NON_MEMBER_ADDRESSES);
278 51
    }
279 51
280 51
    /**
281
     * 会員情報の更新日時が受注の作成日時よりも新しければ, 受注の注文者情報を更新する.
282 51
     *
283 51
     * @param Order $Order
284 42
     * @param Customer $Customer
285 42
     */
286
    public function updateCustomerInfo(Order $Order, Customer $Customer)
287
    {
288 51
        if ($Order->getCreateDate() < $Customer->getUpdateDate()) {
289 51
            $this->setCustomer($Order, $Customer);
290
        }
291
    }
292 51
293
    private function createPreOrderId()
294 51
    {
295
        // ランダムなpre_order_idを作成
296 51
        do {
297 51
            $preOrderId = sha1(StringUtil::random(32));
298 51
299 51
            $Order = $this->orderRepository->findOneBy(
300 51
                [
301 51
                    'pre_order_id' => $preOrderId,
302 51
                    'OrderStatus' => OrderStatus::PROCESSING,
303 51
                ]
304 51
            );
305 51
        } while ($Order);
306
307 51
        return $preOrderId;
308
    }
309
310 51
    private function setCustomer(Order $Order, Customer $Customer)
311
    {
312
        if ($Customer->getId()) {
313 51
            $Order->setCustomer($Customer);
314 51
        }
315
316 51
        $Order->copyProperties(
317 51
            $Customer,
318 51
            [
319 51
                'id',
320
                'create_date',
321
                'update_date',
322
                'del_flg',
323 51
            ]
324
        );
325
    }
326 51
327 51
    /**
328 51
     * @param Collection|ArrayCollection|CartItem[] $CartItems
329
     *
330
     * @return OrderItem[]
331 51
     */
332
    private function createOrderItemsFromCartItems($CartItems)
333 51
    {
334
        $ProductItemType = $this->orderItemTypeRepository->find(OrderItemType::PRODUCT);
335
336 51
        return array_map(function ($item) use ($ProductItemType) {
337
            /* @var $item CartItem */
338 51
            /* @var $ProductClass \Eccube\Entity\ProductClass */
339 51
            $ProductClass = $item->getProductClass();
340 51
            /* @var $Product \Eccube\Entity\Product */
341
            $Product = $ProductClass->getProduct();
342
343
            $OrderItem = new OrderItem();
344 51
            $OrderItem
345 51
                ->setProduct($Product)
346
                ->setProductClass($ProductClass)
347
                ->setProductName($Product->getName())
348
                ->setProductCode($ProductClass->getCode())
349 51
                ->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 51
353
            $ClassCategory1 = $ProductClass->getClassCategory1();
354
            if (!is_null($ClassCategory1)) {
355 51
                $OrderItem->setClasscategoryName1($ClassCategory1->getName());
356 51
                $OrderItem->setClassName1($ClassCategory1->getClassName()->getName());
357 51
            }
358 51
            $ClassCategory2 = $ProductClass->getClassCategory2();
359
            if (!is_null($ClassCategory2)) {
360
                $OrderItem->setClasscategoryName2($ClassCategory2->getName());
361
                $OrderItem->setClassName2($ClassCategory2->getClassName()->getName());
362 51
            }
363
364 51
            return $OrderItem;
365 51
        }, $CartItems instanceof Collection ? $CartItems->toArray() : $CartItems);
366 51
    }
367 51
368 51
    /**
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) {
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) {
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