Passed
Push — master ( 939904...0d717b )
by Andrii
02:21
created

Factory::getSums()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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