Failed Conditions
Pull Request — experimental/sf (#3385)
by chihiro
57:31 queued 48:54
created

StockDiffProcessor   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 157
Duplicated Lines 5.1 %

Coupling/Cohesion

Components 1
Dependencies 3

Test Coverage

Coverage 73.02%

Importance

Changes 0
Metric Value
dl 8
loc 157
rs 10
c 0
b 0
f 0
ccs 46
cts 63
cp 0.7302
wmc 26
lcom 1
cbo 3

7 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
B validate() 0 32 7
B getDiffOfQuantities() 8 26 8
A getQuantityByProductClass() 0 16 4
A prepare() 0 22 4
A commit() 0 5 1
A rollback() 0 5 1

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\PurchaseFlow\Processor;
15
16
use Eccube\Entity\ItemHolderInterface;
17
use Eccube\Entity\Master\OrderStatus;
18
use Eccube\Entity\ProductClass;
19
use Eccube\Entity\ProductStock;
20
use Eccube\Repository\ProductClassRepository;
21
use Eccube\Service\PurchaseFlow\ItemHolderValidator;
22
use Eccube\Service\PurchaseFlow\PurchaseContext;
23
use Eccube\Service\PurchaseFlow\PurchaseException;
24
use Eccube\Service\PurchaseFlow\PurchaseProcessor;
25
26
/**
27
 * 編集前/編集後の個数の差分にもとづいて在庫を更新します.
28
 */
29
class StockDiffProcessor extends ItemHolderValidator implements PurchaseProcessor
30
{
31
    /**
32
     * @var ProductClassRepository
33
     */
34
    protected $productClassRepository;
35
36
    /**
37
     * StockProcessor constructor.
38
     *
39
     * @param ProductClassRepository $productClassRepository
40
     */
41 660
    public function __construct(ProductClassRepository $productClassRepository)
42
    {
43 660
        $this->productClassRepository = $productClassRepository;
44
    }
45
46
    /**
47
     * @param ItemHolderInterface $itemHolder
48
     * @param PurchaseContext $context
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
49
     *
50
     * @throws \Eccube\Service\PurchaseFlow\InvalidItemException
51
     */
52 151
    public function validate(ItemHolderInterface $itemHolder, PurchaseContext $context)
53
    {
54 151
        $From = $context->getOriginHolder();
55 151
        $To = $itemHolder;
56 151
        $diff = $this->getDiffOfQuantities($From, $To);
0 ignored issues
show
Bug introduced by
It seems like $From defined by $context->getOriginHolder() on line 54 can be null; however, Eccube\Service\PurchaseF...::getDiffOfQuantities() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
57
58 151
        foreach ($diff as $id => $quantity) {
59
            /** @var ProductClass $ProductClass */
60 151
            $ProductClass = $this->productClassRepository->find($id);
61 151
            if ($ProductClass->isStockUnlimited()) {
62
                continue;
63
            }
64 151
            $stock = $ProductClass->getStock();
65
            // 更新後ステータスがキャンセルの場合は, 差分ではなく更新後の個数で確認.
66 151
            if ($To->getOrderStatus() && $To->getOrderStatus()->getId() == OrderStatus::CANCEL) {
67
                $Items = $To->getProductOrderItems();
68
                $Items = array_filter($Items, function ($Item) use ($id) {
69
                    return $Item->getProductClass()->getId() == $id;
70
                });
71
                $toQuantity = array_reduce($Items, function ($quantity, $Item) {
72
                    return $quantity += $Item->getQuantity();
73
                }, 0);
74
                if ($stock - $toQuantity < 0) {
75
                    $this->throwInvalidItemException(sprintf('%s: 在庫がたりません', $ProductClass->formatedProductName()));
76
                }
77
            } else {
78 151
                if ($stock - $quantity < 0) {
79 151
                    $this->throwInvalidItemException(sprintf('%s: 在庫がたりません', $ProductClass->formatedProductName()));
80
                }
81
            }
82
        }
83
    }
84
85 151
    protected function getDiffOfQuantities(ItemHolderInterface $From, ItemHolderInterface $To)
86
    {
87 151
        $FromItems = $this->getQuantityByProductClass($From);
88 151
        $ToItems = $this->getQuantityByProductClass($To);
89 151
        $ids = array_unique(array_merge(array_keys($FromItems), array_keys($ToItems)));
90
91 151
        $diff = [];
92 151
        foreach ($ids as $id) {
93
            // 更新された明細
94 151
            if (isset($FromItems[$id]) && isset($ToItems[$id])) {
95
                // 2 -> 3 = +1
96
                // 2 -> 1 = -1
97 149
                $diff[$id] = $ToItems[$id] - $FromItems[$id];
98
            } // 削除された明細
99 5 View Code Duplication
            elseif (isset($FromItems[$id]) && empty($ToItems[$id])) {
100
                // 2 -> 0 = -2
101 3
                $diff[$id] = $FromItems[$id] * -1;
102
            } // 追加された明細
103 5 View Code Duplication
            elseif (!isset($FromItems[$id]) && isset($ToItems[$id])) {
104
                // 0 -> 2 = +2
105 151
                $diff[$id] = $ToItems[$id];
106
            }
107
        }
108
109 151
        return $diff;
110
    }
111
112 151
    protected function getQuantityByProductClass(ItemHolderInterface $ItemHolder)
113
    {
114 151
        $ItemsByProductClass = [];
115 151
        foreach ($ItemHolder->getItems() as $Item) {
116 151
            if ($Item->isProduct()) {
117 151
                $id = $Item->getProductClass()->getId();
118 151
                if (isset($ItemsByProductClass[$id])) {
119
                    $ItemsByProductClass[$id] += $Item->getQuantity();
120
                } else {
121 151
                    $ItemsByProductClass[$id] = $Item->getQuantity();
122
                }
123
            }
124
        }
125
126 151
        return $ItemsByProductClass;
127
    }
128
129
    /**
130
     * 受注の仮確定処理を行います。
131
     *
132
     * @param ItemHolderInterface $target
133
     * @param PurchaseContext $context
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
134
     *
135
     * @throws PurchaseException
136
     */
137 5
    public function prepare(ItemHolderInterface $target, PurchaseContext $context)
0 ignored issues
show
introduced by
Declare public methods first, then protected ones and finally private ones
Loading history...
138
    {
139 5
        $diff = $this->getDiffOfQuantities($context->getOriginHolder(), $target);
0 ignored issues
show
Bug introduced by
It seems like $context->getOriginHolder() can be null; however, getDiffOfQuantities() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
140
141 5
        foreach ($diff as $id => $quantity) {
142
            /** @var ProductClass $ProductClass */
143 5
            $ProductClass = $this->productClassRepository->find($id);
144 5
            if ($ProductClass->isStockUnlimited()) {
145
                continue;
146
            }
147
148 5
            $stock = $ProductClass->getStock() - $quantity;
149 5
            $ProductStock = $ProductClass->getProductStock();
150 5
            if (!$ProductStock) {
151
                $ProductStock = new ProductStock();
152
                $ProductStock->setProductClass($ProductClass);
153
                $ProductClass->setProductStock($ProductStock);
154
            }
155 5
            $ProductClass->setStock($stock);
156 5
            $ProductStock->setStock($stock);
157
        }
158
    }
159
160
    /**
161
     * 受注の確定処理を行います。
162
     *
163
     * @param ItemHolderInterface $target
164
     * @param PurchaseContext $context
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
165
     *
166
     * @throws PurchaseException
167
     */
168 5
    public function commit(ItemHolderInterface $target, PurchaseContext $context)
169
    {
170
        // 何もしない.
171 5
        return;
0 ignored issues
show
Coding Style introduced by
Empty return statement not required here
Loading history...
172
    }
173
174
    /**
175
     * 仮確定した受注データの取り消し処理を行います。
176
     *
177
     * @param ItemHolderInterface $itemHolder
178
     * @param PurchaseContext $context
0 ignored issues
show
introduced by
Expected 5 spaces after parameter type; 1 found
Loading history...
179
     */
180
    public function rollback(ItemHolderInterface $itemHolder, PurchaseContext $context)
181
    {
182
        // 何もしない.
183
        return;
0 ignored issues
show
Coding Style introduced by
Empty return statement not required here
Loading history...
184
    }
185
}
186