Completed
Push — master ( c327dd...cf53d8 )
by Andrii
02:35
created

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