Passed
Push — master ( 1fc46b...24437a )
by Andrii
02:26
created

Factory   D

Complexity

Total Complexity 58

Size/Duplication

Total Lines 258
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 111
c 1
b 0
f 0
dl 0
loc 258
rs 4.5599
wmc 58

27 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 4 1
A parseMoney() 0 7 1
A getPlan() 0 3 1
A getMethod() 0 5 2
B getPrepareMethod() 0 22 9
A extractUnique() 0 16 4
A extractKeys() 0 6 1
A parse() 0 5 2
B getEntityUniqueKeys() 0 22 9
A parseQuantity() 0 7 1
A getMoney() 0 3 1
A parseByUnique() 0 8 2
A find() 0 9 3
A prepareValue() 0 5 2
A getDtoClass() 0 3 1
A createMoney() 0 3 1
A getEntityClass() 0 8 1
A getCustomer() 0 3 1
A create() 0 14 3
A getTarget() 0 3 1
A createUnit() 0 3 1
A createDto() 0 10 2
A getCurrency() 0 3 1
A getType() 0 3 1
A get() 0 15 4
A getQuantity() 0 3 1
A createQuantity() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like Factory often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Factory, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * PHP Billing Library
4
 *
5
 * @link      https://github.com/hiqdev/php-billing
6
 * @package   php-billing
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2017-2018, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\php\billing\tools;
12
13
use Money\Currency;
14
use hiqdev\php\units\Quantity;
15
use hiqdev\php\units\Unit;
16
use Money\Parser\DecimalMoneyParser;
17
use Money\Currencies\ISOCurrencies;
18
19
/**
20
 * Generalized entity factory.
21
 *
22
 * @author Andrii Vasyliev <[email protected]>
23
 */
24
class Factory
25
{
26
    private $entities = [];
27
28
    private $factories = [];
29
30
    protected $moneyParser;
31
32
    public function __construct(array $factories)
33
    {
34
        $this->factories = $factories;
35
        $this->moneyParser = new DecimalMoneyParser(new ISOCurrencies());
36
    }
37
38
    public function getMoney($data)
39
    {
40
        return $this->get('money', $data);
41
    }
42
43
    public function parseMoney($str)
44
    {
45
        [$amount, $currency] = explode(' ', $str);
46
47
        return [
48
            'amount' => $amount,
49
            'currency' => $currency,
50
        ];
51
    }
52
53
    public function createMoney($data)
54
    {
55
        return $this->moneyParser->parse($data['amount'], $data['currency']);
56
    }
57
58
    public function getCurrency($data)
59
    {
60
        return new Currency($data);
61
    }
62
63
    public function getQuantity($data)
64
    {
65
        return $this->get('quantity', $data);
66
    }
67
68
    public function parseQuantity($str)
69
    {
70
        [$quantity, $unit] = explode(' ', $str);
71
72
        return [
73
            'quantity' => $quantity,
74
            'unit' => $unit,
75
        ];
76
    }
77
78
    public function createQuantity($data)
79
    {
80
        return Quantity::create($data['unit'], $data['quantity']);
81
    }
82
83
    public function createUnit($data)
84
    {
85
        return Unit::create($data['name']);
86
    }
87
88
    public function getType($data)
89
    {
90
        return $this->get('type', $data);
91
    }
92
93
    public function getTarget($data)
94
    {
95
        return $this->get('target', $data);
96
    }
97
98
    public function getPlan($data)
99
    {
100
        return $this->get('plan', $data);
101
    }
102
103
    public function getCustomer($data)
104
    {
105
        return $this->get('customer', $data);
106
    }
107
108
    public function get(string $entity, $data)
109
    {
110
        if (is_scalar($data)) {
111
            $data = $this->parse($entity, $data);
112
        }
113
114
        $keys = $this->extractKeys($entity, $data);
115
116
        $res = $this->find($entity, $keys) ?: $this->create($entity, $data);
117
118
        foreach ($keys as $key) {
119
            $this->entities[$entity][$key] = $res;
120
        }
121
122
        return $res;
123
    }
124
125
    public function parse(string $entity, $str)
126
    {
127
        $method = $this->getMethod($entity, 'parse');
128
129
        return $method ? $this->{$method}($str) : $this->parseByUnique($entity, $str);
130
    }
131
132
    public function parseByUnique(string $entity, $str)
133
    {
134
        $keys = $this->getEntityUniqueKeys($entity);
135
        if (count($keys) === 1) {
136
            return [reset($keys) => $str];
137
        }
138
139
        return ['id' => $str];
140
    }
141
142
    public function find(string $entity, array $keys)
143
    {
144
        foreach ($keys as $key) {
145
            if (!empty($this->entities[$entity][$key])) {
146
                return $this->entities[$entity][$key];
147
            }
148
        }
149
150
        return null;
151
    }
152
153
    public function create(string $entity, $data)
154
    {
155
        $method = $this->getMethod($entity, 'create');
156
        if ($method) {
157
            return $this->{$method}($data);
158
        }
159
160
        if (empty($this->factories[$entity])) {
161
            throw new FactoryNotFoundException($entity);
0 ignored issues
show
Bug introduced by
The type hiqdev\php\billing\tools\FactoryNotFoundException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
162
        }
163
164
        $factory = $this->factories[$entity];
165
166
        return $factory->create($this->createDto($entity, $data));
167
    }
168
169
    public function createDto(string $entity, array $data)
170
    {
171
        $class = $this->getDtoClass($entity);
172
        $dto = new $class();
173
174
        foreach ($data as $key => $value) {
175
            $dto->{$key} = $this->prepareValue($entity, $key, $value);
176
        }
177
178
        return $dto;
179
    }
180
181
    public function getDtoClass(string $entity)
182
    {
183
        return $this->getEntityClass($entity) . 'CreationDto';
184
    }
185
186
    public function prepareValue($entity, $key, $value)
187
    {
188
        $method = $this->getPrepareMethod($entity, $key);
189
190
        return $method ? $this->{$method}($value) : $value;
191
    }
192
193
    private function getMethod(string $entity, string $op)
194
    {
195
        $method = $op . ucfirst($entity);
196
197
        return method_exists($this, $method) ? $method : null;
198
    }
199
200
    private function getPrepareMethod(string $entity, string $key)
201
    {
202
        switch ($key) {
203
            case 'seller':
204
                return 'getCustomer';
205
            case 'plan':
206
                return 'getPlan';
207
            case 'type':
208
                return 'getType';
209
            case 'target':
210
                return 'getTarget';
211
            case 'price':
212
                return 'getMoney';
213
            case 'currency':
214
                return 'getCurrency';
215
            case 'prepaid':
216
                return 'getQuantity';
217
            case 'unit':
218
                return 'getUnit';
219
        }
220
221
        return null;
222
    }
223
224
    public function getEntityClass(string $entity)
225
    {
226
        $parts = explode('\\', __NAMESPACE__);
227
        array_pop($parts);
228
        $parts[] = $entity;
229
        $parts[] = ucfirst($entity);
230
231
        return implode('\\', $parts);
232
    }
233
234
    public function extractKeys(string $entity, $data)
235
    {
236
        $id = $data['id'] ?? null;
237
        $unique = $this->extractUnique($entity, $data);
238
239
        return array_filter(['id' => $id, 'unique' => $unique]);
240
    }
241
242
    public function extractUnique(string $entity, $data)
243
    {
244
        $keys = $this->getEntityUniqueKeys($entity);
245
        if (empty($keys)) {
246
            return null;
247
        }
248
249
        $values = [];
250
        foreach ($keys as $key) {
251
            if (empty($data[$key])) {
252
                return null;
253
            }
254
            $values[$key] = $data[$key];
255
        }
256
257
        return implode(' ', $values);
258
    }
259
260
    public function getEntityUniqueKeys(string $entity): array
261
    {
262
        switch ($entity) {
263
            case 'customer':
264
                return ['login'];
265
            case 'type':
266
                return ['name'];
267
            case 'plan':
268
                return ['name', 'seller'];
269
            case 'price':
270
                return [];
271
            case 'target':
272
                return ['type', 'name'];
273
            case 'money':
274
                return ['amount', 'currency'];
275
            case 'unit':
276
                return ['name'];
277
            case 'quantity':
278
                return ['quantity', 'unit'];
279
        }
280
281
        throw new UnknownEntityException($entity);
0 ignored issues
show
Bug introduced by
The type hiqdev\php\billing\tools\UnknownEntityException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
282
    }
283
}
284