Passed
Push — master ( 27707a...e0de69 )
by Andrii
02:47
created

Factory::getPrice()   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 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 getPrice($data)
162
    {
163
        return $this->get('price', $data);
164
    }
165
166
    public function getBill($data)
167
    {
168
        return $this->get('bill', $data);
169
    }
170
171
    public function getPlan($data)
172
    {
173
        return $this->get('plan', $data);
174
    }
175
176
    public function getSale($data)
177
    {
178
        return $this->get('sale', $data);
179
    }
180
181
    public function getCustomer($data)
182
    {
183
        return $this->get('customer', $data);
184
    }
185
186
    public function get(string $entity, $data)
187
    {
188
        if (is_scalar($data)) {
189
            $data = $this->parse($entity, $data);
190
        }
191
192
        $keys = $this->extractKeys($entity, $data);
193
194
        $res = $this->find($entity, $keys) ?: $this->create($entity, $data);
195
196
        foreach ($keys as $key) {
197
            $this->entities[$entity][$key] = $res;
198
        }
199
200
        return $res;
201
    }
202
203
    public function parse(string $entity, $str)
204
    {
205
        $method = $this->getMethod($entity, 'parse');
206
207
        return $method ? $this->{$method}($str) : $this->parseByUnique($entity, $str);
208
    }
209
210
    public function parseByUnique(string $entity, $str, $delimiter = ':')
211
    {
212
        $keys = $this->getEntityUniqueKeys($entity);
213
        if (count($keys) === 1) {
214
            return [reset($keys) => $str];
215
        }
216
        $parts = explode($delimiter, $str, count($keys));
217
        if (count($parts) === count($keys)) {
218
            $res = array_combine($keys, $parts);
219
        } else {
220
            $res = [];
221
        }
222
        $res['id'] = $str;
223
224
        return $res;
225
    }
226
227
    public function find(string $entity, array $keys)
228
    {
229
        foreach ($keys as $key) {
230
            if (!empty($this->entities[$entity][$key])) {
231
                return $this->entities[$entity][$key];
232
            }
233
        }
234
235
        return null;
236
    }
237
238
    public function create(string $entity, $data)
239
    {
240
        $method = $this->getMethod($entity, 'create');
241
        if ($method) {
242
            return $this->{$method}($data);
243
        }
244
245
        if (empty($this->factories[$entity])) {
246
            throw new FactoryNotFoundException($entity);
247
        }
248
249
        $factory = $this->factories[$entity];
250
251
        return $factory->create($this->createDto($entity, $data));
252
    }
253
254
    public function createDto(string $entity, array $data)
255
    {
256
        $class = $this->getDtoClass($entity);
257
        $dto = new $class();
258
259
        foreach ($data as $key => $value) {
260
            $dto->{$key} = $this->prepareValue($entity, $key, $value);
261
        }
262
263
        return $dto;
264
    }
265
266
    public function getDtoClass(string $entity)
267
    {
268
        return $this->getEntityClass($entity) . 'CreationDto';
269
    }
270
271
    public function prepareValue($entity, $key, $value)
272
    {
273
        if (is_object($value)) {
274
            return $value;
275
        }
276
        $method = $this->getPrepareMethod($entity, $key);
277
278
        return $method ? $this->{$method}($value) : $value;
279
    }
280
281
    private function getMethod(string $entity, string $op)
282
    {
283
        $method = $op . ucfirst($entity);
284
285
        return method_exists($this, $method) ? $method : null;
286
    }
287
288
    private $prepareMethods = [
289
        'action'    => 'getAction',
290
        'bill'      => 'getBill',
291
        'charge'    => 'getCharge',
292
        'charges'   => 'getCharges',
293
        'currency'  => 'getCurrency',
294
        'customer'  => 'getCustomer',
295
        'plan'      => 'getPlan',
296
        'prepaid'   => 'getQuantity',
297
        'price'     => 'getMoney',
298
        'quantity'  => 'getQuantity',
299
        'sale'      => 'getSale',
300
        'seller'    => 'getCustomer',
301
        'sum'       => 'getMoney',
302
        'sums'      => 'getSums',
303
        'target'    => 'getTarget',
304
        'targets'   => 'getTargets',
305
        'time'      => 'getTime',
306
        'type'      => 'getType',
307
        'unit'      => 'getUnit',
308
        'usage'     => 'getQuantity',
309
    ];
310
311
    private function getPrepareMethod(string $entity, string $key)
312
    {
313
        if ($entity === 'charge' && $key === 'price') {
314
            return 'getPrice';
315
        }
316
        if ($entity === 'target' && $key === 'type') {
317
            return null;
318
        }
319
        return $this->prepareMethods[$key] ?? null;
320
    }
321
322
    public function getEntityClass(string $entity)
323
    {
324
        $parts = explode('\\', __NAMESPACE__);
325
        array_pop($parts);
326
        $parts[] = $entity;
327
        $parts[] = ucfirst($entity);
328
329
        return implode('\\', $parts);
330
    }
331
332
    public function extractKeys(string $entity, $data)
333
    {
334
        $id = $data['id'] ?? null;
335
        $unique = $this->extractUnique($entity, $data);
336
337
        return array_filter(['id' => $id, 'unique' => $unique]);
338
    }
339
340
    public function extractUnique(string $entity, $data)
341
    {
342
        $keys = $this->getEntityUniqueKeys($entity);
343
        if (empty($keys)) {
344
            return null;
345
        }
346
347
        $values = [];
348
        foreach ($keys as $key) {
349
            if (empty($data[$key])) {
350
                return null;
351
            }
352
            $values[$key] = $data[$key];
353
        }
354
355
        return implode(' ', $values);
356
    }
357
358
359
    private $uniqueKeys = [
360
        'action'    => [],
361
        'bill'      => [],
362
        'charge'    => [],
363
        'customer'  => ['login'],
364
        'money'     => ['amount', 'currency'],
365
        'plan'      => ['name', 'seller'],
366
        'price'     => [],
367
        'quantity'  => ['quantity', 'unit'],
368
        'sale'      => [],
369
        'targets'   => [],
370
        'target'    => ['type', 'name'],
371
        'time'      => ['date'],
372
        'type'      => ['name'],
373
        'unit'      => ['name'],
374
    ];
375
376
    public function getEntityUniqueKeys(string $entity): array
377
    {
378
        $keys = $this->uniqueKeys[$entity] ?? null;
379
380
        if (is_null($keys)) {
381
            throw new UnknownEntityException($entity);
382
        }
383
384
        return $keys;
385
    }
386
}
387