Failed Conditions
Push — sf/last-boss ( 53d963...58156b )
by Kiyotaka
09:54 queued 04:25
created

DeliveryFeePreprocessor   A

Complexity

Total Complexity 11

Size/Duplication

Total Lines 117
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
dl 0
loc 117
rs 10
c 0
b 0
f 0
wmc 11
lcom 1
cbo 8

4 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 1
A process() 0 5 1
A removeDeliveryFeeItem() 0 12 4
B saveDeliveryFeeItem() 0 46 5
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\PurchaseFlow\Processor;
15
16
use Doctrine\ORM\EntityManagerInterface;
17
use Eccube\Entity\BaseInfo;
18
use Eccube\Entity\DeliveryFee;
19
use Eccube\Entity\ItemHolderInterface;
20
use Eccube\Entity\Master\OrderItemType;
21
use Eccube\Entity\Master\TaxDisplayType;
22
use Eccube\Entity\Master\TaxType;
23
use Eccube\Entity\Order;
24
use Eccube\Entity\OrderItem;
25
use Eccube\Entity\Shipping;
26
use Eccube\Repository\BaseInfoRepository;
27
use Eccube\Repository\DeliveryFeeRepository;
28
use Eccube\Repository\TaxRuleRepository;
29
use Eccube\Service\PurchaseFlow\ItemHolderPreprocessor;
30
use Eccube\Service\PurchaseFlow\PurchaseContext;
31
32
/**
33
 * 送料明細追加.
34
 */
35
class DeliveryFeePreprocessor implements ItemHolderPreprocessor
36
{
37
    /** @var BaseInfo */
38
    protected $BaseInfo;
39
40
    /**
41
     * @var EntityManagerInterface
42
     */
43
    protected $entityManager;
44
45
    /**
46
     * @var TaxRuleRepository
47
     */
48
    protected $taxRuleRepository;
49
50
    /**
51
     * @var DeliveryFeeRepository
52
     */
53
    protected $deliveryFeeRepository;
54
55
    /**
56
     * DeliveryFeePreprocessor constructor.
57
     *
58
     * @param BaseInfoRepository $baseInfoRepository
59
     * @param EntityManagerInterface $entityManager
60
     * @param TaxRuleRepository $taxRuleRepository
61
     * @param DeliveryFeeRepository $deliveryFeeRepository
62
     */
63
    public function __construct(
64
        BaseInfoRepository $baseInfoRepository,
65
        EntityManagerInterface $entityManager,
66
        TaxRuleRepository $taxRuleRepository,
67
        DeliveryFeeRepository $deliveryFeeRepository
68
    ) {
69
        $this->BaseInfo = $baseInfoRepository->get();
70
        $this->entityManager = $entityManager;
71
        $this->taxRuleRepository = $taxRuleRepository;
72
        $this->deliveryFeeRepository = $deliveryFeeRepository;
73
    }
74
75
    /**
76
     * @param ItemHolderInterface $itemHolder
77
     * @param PurchaseContext $context
78
     *
79
     * @throws \Doctrine\ORM\NoResultException
80
     */
81
    public function process(ItemHolderInterface $itemHolder, PurchaseContext $context)
82
    {
83
        $this->removeDeliveryFeeItem($itemHolder);
84
        $this->saveDeliveryFeeItem($itemHolder);
85
    }
86
87
    private function removeDeliveryFeeItem(ItemHolderInterface $itemHolder)
88
    {
89
        foreach ($itemHolder->getShippings() as $Shipping) {
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Eccube\Entity\ItemHolderInterface as the method getShippings() does only exist in the following implementations of said interface: Eccube\Entity\Order.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
90
            foreach ($Shipping->getOrderItems() as $item) {
91
                if ($item->getProcessorName() == DeliveryFeePreprocessor::class) {
92
                    $Shipping->removeOrderItem($item);
93
                    $itemHolder->removeOrderItem($item);
0 ignored issues
show
Bug introduced by
It seems like you code against a concrete implementation and not the interface Eccube\Entity\ItemHolderInterface as the method removeOrderItem() does only exist in the following implementations of said interface: Eccube\Entity\Order.

Let’s take a look at an example:

interface User
{
    /** @return string */
    public function getPassword();
}

class MyUser implements User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different implementation of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the interface:

    interface User
    {
        /** @return string */
        public function getPassword();
    
        /** @return string */
        public function getDisplayName();
    }
    
Loading history...
94
                    $this->entityManager->remove($item);
95
                }
96
            }
97
        }
98
    }
99
100
    /**
101
     * @param ItemHolderInterface $itemHolder
102
     *
103
     * @throws \Doctrine\ORM\NoResultException
104
     */
105
    private function saveDeliveryFeeItem(ItemHolderInterface $itemHolder)
106
    {
107
        $DeliveryFeeType = $this->entityManager
108
            ->find(OrderItemType::class, OrderItemType::DELIVERY_FEE);
109
        $TaxInclude = $this->entityManager
110
            ->find(TaxDisplayType::class, TaxDisplayType::INCLUDED);
111
        $Taxion = $this->entityManager
112
            ->find(TaxType::class, TaxType::TAXATION);
113
114
        /** @var Order $Order */
115
        $Order = $itemHolder;
116
        /* @var Shipping $Shipping */
117
        foreach ($Order->getShippings() as $Shipping) {
118
            // 送料の計算
119
            $deliveryFeeProduct = 0;
120
            if ($this->BaseInfo->isOptionProductDeliveryFee()) {
121
                /** @var OrderItem $item */
122
                foreach ($Shipping->getOrderItems() as $item) {
123
                    if (!$item->isProduct()) {
124
                        continue;
125
                    }
126
                    $deliveryFeeProduct += $item->getProductClass()->getDeliveryFee() * $item->getQuantity();
127
                }
128
            }
129
130
            /** @var DeliveryFee $DeliveryFee */
131
            $DeliveryFee = $this->deliveryFeeRepository->findOneBy([
132
                'Delivery' => $Shipping->getDelivery(),
133
                'Pref' => $Shipping->getPref(),
134
            ]);
135
136
            $OrderItem = new OrderItem();
137
            $OrderItem->setProductName($DeliveryFeeType->getName())
138
                ->setPrice($DeliveryFee->getFee() + $deliveryFeeProduct)
139
                ->setQuantity(1)
140
                ->setOrderItemType($DeliveryFeeType)
141
                ->setShipping($Shipping)
142
                ->setOrder($itemHolder)
0 ignored issues
show
Documentation introduced by
$itemHolder is of type object<Eccube\Entity\ItemHolderInterface>, but the function expects a null|object<Eccube\Entity\Order>.

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...
143
                ->setTaxDisplayType($TaxInclude)
144
                ->setTaxType($Taxion)
145
                ->setProcessorName(DeliveryFeePreprocessor::class);
146
147
            $itemHolder->addItem($OrderItem);
148
            $Shipping->addOrderItem($OrderItem);
149
        }
150
    }
151
}
152