Passed
Push — master ( fd2f7c...a88c1a )
by Andrii
02:45
created

Factory::getPlan()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
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 DateTimeImmutable;
14
use Money\Currency;
15
use hiqdev\php\units\Quantity;
16
use hiqdev\php\units\Unit;
17
use Money\Parser\DecimalMoneyParser;
18
use Money\Currencies\ISOCurrencies;
19
use hiqdev\php\billing\Exception\UnknownEntityException;
20
use hiqdev\php\billing\target\TargetCollection;
21
22
/**
23
 * Generalized entity factory.
24
 *
25
 * @author Andrii Vasyliev <[email protected]>
26
 */
27
class Factory
28
{
29
    private $entities = [];
30
31
    private $factories = [];
32
33
    protected $moneyParser;
34
35
    public function __construct(array $factories)
36
    {
37
        $this->factories = $factories;
38
        $this->moneyParser = new DecimalMoneyParser(new ISOCurrencies());
39
    }
40
41
    public function getMoney($data)
42
    {
43
        return $this->get('money', $data);
44
    }
45
46
    public function getSums($data)
47
    {
48
        $res = [];
49
        foreach ($data as $key => $value) {
50
            $res[$key] = $value*100;
51
        }
52
53
        return $res;
54
    }
55
56
    public function parseMoney($str)
57
    {
58
        [$amount, $currency] = explode(' ', $str);
59
60
        return [
61
            'amount' => $amount,
62
            'currency' => $currency,
63
        ];
64
    }
65
66
    public function createMoney($data)
67
    {
68
        return $this->moneyParser->parse($data['amount'], $data['currency']);
69
    }
70
71
    public function getCurrency($data)
72
    {
73
        return new Currency($data);
74
    }
75
76
    public function getQuantity($data)
77
    {
78
        return $this->get('quantity', $data);
79
    }
80
81
    public function parseQuantity($str)
82
    {
83
        [$quantity, $unit] = explode(' ', $str);
84
85
        return [
86
            'quantity' => $quantity,
87
            'unit' => $unit,
88
        ];
89
    }
90
91
    public function createQuantity($data)
92
    {
93
        return Quantity::create($data['unit'], $data['quantity']);
94
    }
95
96
    public function getUnit($data)
97
    {
98
        return $this->get('unit', $data);
99
    }
100
101
    public function createUnit($data)
102
    {
103
        return Unit::create($data['name']);
104
    }
105
106
    public function getType($data)
107
    {
108
        return $this->get('type', $data);
109
    }
110
111
    public function getTime($data)
112
    {
113
        return $this->get('time', $data);
114
    }
115
116
    public function createTime($data)
117
    {
118
        return new DateTimeImmutable($data['time']);
119
    }
120
121
    public function getTargets($data)
122
    {
123
        return $this->get('targets', $data);
124
    }
125
126
    public function createTargets($data)
127
    {
128
        $targets = [];
129
        foreach ($data as $one) {
130
            $targets[] = $this->getTarget($one);
131
        }
132
133
        return new TargetCollection($targets);
134
    }
135
136
    public function getTarget($data)
137
    {
138
        return $this->get('target', $data);
139
    }
140
141
    public function getPlan($data)
142
    {
143
        return $this->get('plan', $data);
144
    }
145
146
    public function getSale($data)
147
    {
148
        return $this->get('sale', $data);
149
    }
150
151
    public function getCustomer($data)
152
    {
153
        return $this->get('customer', $data);
154
    }
155
156
    public function get(string $entity, $data)
157
    {
158
        if (is_scalar($data)) {
159
            $data = $this->parse($entity, $data);
160
        }
161
162
        $keys = $this->extractKeys($entity, $data);
163
164
        $res = $this->find($entity, $keys) ?: $this->create($entity, $data);
165
166
        foreach ($keys as $key) {
167
            $this->entities[$entity][$key] = $res;
168
        }
169
170
        return $res;
171
    }
172
173
    public function parse(string $entity, $str)
174
    {
175
        $method = $this->getMethod($entity, 'parse');
176
177
        return $method ? $this->{$method}($str) : $this->parseByUnique($entity, $str);
178
    }
179
180
    public function parseByUnique(string $entity, $str)
181
    {
182
        $keys = $this->getEntityUniqueKeys($entity);
183
        if (count($keys) === 1) {
184
            return [reset($keys) => $str];
185
        }
186
187
        return ['id' => $str];
188
    }
189
190
    public function find(string $entity, array $keys)
191
    {
192
        foreach ($keys as $key) {
193
            if (!empty($this->entities[$entity][$key])) {
194
                return $this->entities[$entity][$key];
195
            }
196
        }
197
198
        return null;
199
    }
200
201
    public function create(string $entity, $data)
202
    {
203
        $method = $this->getMethod($entity, 'create');
204
        if ($method) {
205
            return $this->{$method}($data);
206
        }
207
208
        if (empty($this->factories[$entity])) {
209
            throw new FactoryNotFoundException($entity);
210
        }
211
212
        $factory = $this->factories[$entity];
213
214
        return $factory->create($this->createDto($entity, $data));
215
    }
216
217
    public function createDto(string $entity, array $data)
218
    {
219
        $class = $this->getDtoClass($entity);
220
        $dto = new $class();
221
222
        foreach ($data as $key => $value) {
223
            $dto->{$key} = $this->prepareValue($entity, $key, $value);
224
        }
225
226
        return $dto;
227
    }
228
229
    public function getDtoClass(string $entity)
230
    {
231
        return $this->getEntityClass($entity) . 'CreationDto';
232
    }
233
234
    public function prepareValue($entity, $key, $value)
235
    {
236
        if (is_object($value)) {
237
            return $value;
238
        }
239
        $method = $this->getPrepareMethod($entity, $key);
240
241
        return $method ? $this->{$method}($value) : $value;
242
    }
243
244
    private function getMethod(string $entity, string $op)
245
    {
246
        $method = $op . ucfirst($entity);
247
248
        return method_exists($this, $method) ? $method : null;
249
    }
250
251
    private $prepareMethods = [
252
        'bill'      => 'getBill',
253
        'charge'    => 'getCharge',
254
        'currency'  => 'getCurrency',
255
        'customer'  => 'getCustomer',
256
        'plan'      => 'getPlan',
257
        'prepaid'   => 'getQuantity',
258
        'price'     => 'getMoney',
259
        'quantity'  => 'getQuantity',
260
        'sale'      => 'getSale',
261
        'seller'    => 'getCustomer',
262
        'sum'       => 'getMoney',
263
        'sums'      => 'getSums',
264
        'target'    => 'getTarget',
265
        'targets'   => 'getTargets',
266
        'time'      => 'getTime',
267
        'type'      => 'getType',
268
        'unit'      => 'getUnit',
269
    ];
270
271
    private function getPrepareMethod(string $entity, string $key)
272
    {
273
        return $this->prepareMethods[$key] ?? null;
274
    }
275
276
    public function getEntityClass(string $entity)
277
    {
278
        $parts = explode('\\', __NAMESPACE__);
279
        array_pop($parts);
280
        $parts[] = $entity;
281
        $parts[] = ucfirst($entity);
282
283
        return implode('\\', $parts);
284
    }
285
286
    public function extractKeys(string $entity, $data)
287
    {
288
        $id = $data['id'] ?? null;
289
        $unique = $this->extractUnique($entity, $data);
290
291
        return array_filter(['id' => $id, 'unique' => $unique]);
292
    }
293
294
    public function extractUnique(string $entity, $data)
295
    {
296
        $keys = $this->getEntityUniqueKeys($entity);
297
        if (empty($keys)) {
298
            return null;
299
        }
300
301
        $values = [];
302
        foreach ($keys as $key) {
303
            if (empty($data[$key])) {
304
                return null;
305
            }
306
            $values[$key] = $data[$key];
307
        }
308
309
        return implode(' ', $values);
310
    }
311
312
313
    private $uniqueKeys = [
314
        'action'    => [],
315
        'bill'      => [],
316
        'customer'  => ['login'],
317
        'money'     => ['amount', 'currency'],
318
        'plan'      => ['name', 'seller'],
319
        'price'     => [],
320
        'quantity'  => ['quantity', 'unit'],
321
        'sale'      => [],
322
        'targets'   => [],
323
        'target'    => ['type', 'name'],
324
        'time'      => ['time'],
325
        'type'      => ['name'],
326
        'unit'      => ['name'],
327
    ];
328
329
    public function getEntityUniqueKeys(string $entity): array
330
    {
331
        $keys = $this->uniqueKeys[$entity] ?? null;
332
333
        if (is_null($keys)) {
334
            throw new UnknownEntityException($entity);
335
        }
336
337
        return $keys;
338
    }
339
}
340