Passed
Push — main ( f9384e...7dc7cf )
by Pranjal
02:26
created

Model::set()   C

Complexity

Conditions 12
Paths 60

Size

Total Lines 55
Code Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 2 Features 0
Metric Value
cc 12
eloc 35
c 4
b 2
f 0
nc 60
nop 2
dl 0
loc 55
rs 6.9666

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
declare(strict_types=1);
3
namespace Scrawler\Arca;
4
5
use Scrawler\Arca\Connection;
6
use Doctrine\DBAL\Types\Type;
7
8
9
/**
10
 * Model class that represents single record in database
11
 */
12
class Model
13
{
14
    /**
15
     * Store all properties of model
16
     * @var array<string,mixed>
17
     */
18
    private array $__properties = array();
19
20
    /**
21
     * Store all properties with all relation
22
     * @var array<string,mixed>
23
     */
24
    private array $__props = array();
25
26
27
    /**
28
     * Store all types of properties
29
     * @var array<string,mixed>
30
     */
31
    private array $__types = array();
32
33
    /**
34
     * Store the table name of model
35
     * @var string
36
     */
37
    private string $table;
38
    /**
39
     * Store the metadata of model
40
     * @var array<string,mixed>
41
     */
42
    private array $__meta = [];
43
    /**
44
     * Store the connection
45
     * @var Connection
46
     */
47
    private Connection $connection;
48
49
50
    /**
51
     * Create a new model
52
     * @param string $name
53
     * @param Connection $connection
54
     */
55
    public function __construct(string $name, Connection $connection)
56
    {
57
58
        $this->table = $name;
59
        $this->connection = $connection;
60
        $this->__meta['has_foreign']['oto'] = false;
61
        $this->__meta['has_foreign']['otm'] = false;
62
        $this->__meta['has_foreign']['mtm'] = false;
63
        $this->__meta['is_loaded'] = false;
64
        $this->__meta['id_error'] = false;
65
        $this->__meta['foreign_models']['otm'] = [];
66
        $this->__meta['foreign_models']['oto'] = [];
67
        $this->__meta['foreign_models']['mtm'] = [];
68
        $this->__meta['id'] = 0;
69
    }
70
71
    /**
72
     * adds the key to properties
73
     * @param string $key
74
     * @param mixed $val
75
     * @return void
76
     */
77
    public function __set(string $key, mixed $val): void
78
    {
79
        $this->set($key, $val);
80
    }
81
82
    /**
83
     * Adds the key to properties
84
     * @param string $key
85
     * @param mixed $val
86
     * @return void
87
     */
88
    public function set(string $key, mixed $val): void
89
    {
90
        if ($key === 'id') {
91
            $this->__meta['id'] = $val;
92
            $this->__meta['id_error'] = true;
93
        }
94
95
96
        if (\Safe\preg_match('/[A-Z]/', $key)) {
97
            $parts = \Safe\preg_split('/(?=[A-Z])/', $key, -1, PREG_SPLIT_NO_EMPTY);
98
            if (strtolower($parts[0]) === 'own') {
99
                $val = $this->arrayToCollection($val);
100
                array_push($this->__meta['foreign_models']['otm'], $val);
101
                $this->__meta['has_foreign']['otm'] = true;
102
103
                $this->__props[$key] = $val;
104
                return;
105
            }
106
            if (strtolower($parts[0]) === 'shared') {
107
                    $this->arrayToCollection($val);
108
                    array_push($this->__meta['foreign_models']['mtm'], $val);
109
                    $this->__meta['has_foreign']['mtm'] = true;
110
                   
111
                    $this->__props[$key] = $val;
112
                return;
113
            }
114
        }
115
        if ($val instanceof Model) {
116
            if (isset($this->__props[$key . '_id'])) {
117
                unset($this->__props[$key . '_id']);
118
            }
119
            $this->__meta['has_foreign']['oto'] = true;
120
            array_push($this->__meta['foreign_models']['oto'], $val);
121
            $this->__props[$key] = $val;
122
            return;
123
        }
124
125
        $type = gettype($val);
126
127
        if (gettype($val) == 'boolean') {
128
            ($val) ? $val = 1 : $val = 0;
129
        }
130
131
        if ($type == 'array' || $type == 'object') {
132
            $val = Type::getType('json_document')->convertToDatabaseValue($val, $this->connection->getDatabasePlatform());
0 ignored issues
show
Bug introduced by
The method getDatabasePlatform() does not exist on Scrawler\Arca\Connection. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

132
            $val = Type::getType('json_document')->convertToDatabaseValue($val, $this->connection->/** @scrutinizer ignore-call */ getDatabasePlatform());
Loading history...
133
            $type = 'json_document';
134
        }
135
136
        if ($type == 'string') {
137
            $type = 'text';
138
        }
139
140
        $this->__properties[$key] = $val;
141
        $this->__props[$key] = $val;
142
        $this->__types[$key] = $type;
143
144
    }
145
146
    /**
147
     * Check if array passed is instance of model
148
     * @param array<mixed>|Collection $models
149
     * @throws \Scrawler\Arca\Exception\InvalidModelException
150
     * @return Collection
151
     */
152
    private function arrayToCollection(array|Collection $models): Collection
153
    {
154
        if ($models instanceof Collection) {
0 ignored issues
show
introduced by
$models is never a sub-type of Scrawler\Arca\Collection.
Loading history...
155
            return $models;
156
        }
157
158
        if (count(array_filter($models, fn($d) => !$d instanceof Model)) > 0) {
159
            throw new Exception\InvalidModelException();
160
        }
161
162
        return Collection::fromIterable($models);
163
164
165
    }
166
167
    /**
168
     * Get a key from properties, keys can be relational
169
     * like sharedList,ownList or foreign table
170
     * @param string $key
171
     * @return mixed
172
     */
173
    public function __get(string $key): mixed
174
    {
175
        return $this->get($key);
176
    }
177
178
    /**
179
     * Get a key from properties, keys can be relational
180
     * like sharedList,ownList or foreign table
181
     * @param string $key
182
     * @return mixed
183
     */
184
    public function get(string $key): mixed
185
    {
186
        if (\Safe\preg_match('/[A-Z]/', $key)) {
187
            $parts = \Safe\preg_split('/(?=[A-Z])/', $key, -1, PREG_SPLIT_NO_EMPTY);
188
            if (strtolower($parts[0]) == 'own') {
189
                if (strtolower($parts[2]) == 'list') {
190
                    $result = $this->connection->getRecordManager()->find(strtolower($parts[1]))->where($this->getName() . '_id = "' . $this->__meta['id'] . '"')->get();
191
                    $this->set($key, $result);
192
                    return $result;
193
                }
194
            }
195
            if (strtolower($parts[0]) == 'shared') {
196
                if (strtolower($parts[2]) == 'list') {
197
                    $rel_table = $this->connection->getTableManager()->tableExists($this->table . '_' . strtolower($parts[1])) ? $this->table . '_' . strtolower($parts[1]) : strtolower($parts[1]) . '_' . $this->table;
198
                    $relations = $this->connection->getRecordManager()->find($rel_table)->where($this->getName() . '_id = "' . $this->__meta['id'] . '"')->get();
199
                    $rel_ids = '';
200
                    foreach ($relations as $relation) {
201
                        $key = strtolower($parts[1]) . '_id';
202
                        $rel_ids .= "'" . $relation->$key . "',";
203
                    }
204
                    $rel_ids = substr($rel_ids, 0, -1);
205
                    $result = $this->connection->getRecordManager()->find(strtolower($parts[1]))->where('id IN (' . $rel_ids . ')')->get();
206
                    $this->set($key, $result);
207
                    return $result;
208
                }
209
            }
210
        }
211
212
        if (array_key_exists($key . '_id', $this->__properties)) {
213
            $result = $this->connection->getRecordManager()->getById($key, $this->__properties[$key . '_id']);
214
            $this->set($key, $result);
215
            return $result;
216
        }
217
218
        if (array_key_exists($key, $this->__properties)) {
219
            $type = Type::getType($this->connection->getTableManager()->getTable($this->table)->getColumn($key)->getComment());
220
            $result = $type->convertToPHPValue($this->__properties[$key], $this->connection->getDatabasePlatform());
221
            $this->set($key, $result);
222
            return $result;
223
        }
224
225
        throw new Exception\KeyNotFoundException();
226
    }
227
228
    /**
229
     * Eager Load relation variable
230
     * @param array<string> $relations
231
     * @return Model
232
     */
233
    public function with(array $relations): Model
234
    {
235
        foreach ($relations as $relation) {
236
            $this->get($relation);
237
        }
238
        return $this;
239
    }
240
241
242
    /**
243
     * Unset a property from model
244
     * @param string $key
245
     * @return void
246
     */
247
    public function __unset(string $key): void
248
    {
249
        $this->unset($key);
250
    }
251
252
    /**
253
     * Unset a property from model
254
     * @param string $key
255
     * @return void
256
     */
257
    public function unset(string $key): void
258
    {
259
        unset($this->__properties[$key]);
260
        unset($this->__props[$key]);
261
    }
262
263
    /**
264
     * Check if property exists
265
     * @param string $key
266
     * @return bool
267
     */
268
    public function __isset(string $key): bool
269
    {
270
        return $this->isset($key);
271
    }
272
273
    /**
274
     * Check if property exists
275
     *
276
     * @param string $key
277
     * @return bool
278
     */
279
    public function isset(string $key): bool
280
    {
281
        return array_key_exists($key, $this->__props);
282
    }
283
284
    /**
285
     * Set all properties of model via array
286
     * @param array<mixed> $properties
287
     * @return Model
288
     */
289
    public function setProperties(array $properties): Model
290
    {
291
        foreach ($properties as $key => $value) {
292
            $this->set($key, $value);
293
        }
294
        return $this;
295
    }
296
297
    /**
298
     * Set all properties of model loaded via database
299
     * @param array<mixed> $properties
300
     * @return Model
301
     */
302
    public function setLoadedProperties(array $properties): Model
303
    {
304
        $this->__properties = $properties;
305
        $this->__props = $properties;
306
        foreach ($properties as $key => $value) {
307
            $this->__types[$key] = $this->connection->getTableManager()->getTable($this->table)->getColumn($key)->getComment();
308
        }
309
        $this->__meta['id'] = $properties['id'];
310
        return $this;
311
    }
312
313
314
    /**
315
     * Get all properties with relational models in array form
316
     * @return array<mixed>
317
     */
318
    public function getProperties(): array
319
    {
320
        return $this->__props;
321
    }
322
323
    /**
324
     * Get self properties without relations in array form
325
     * @return array<mixed>
326
     */
327
    public function getSelfProperties(): array
328
    {
329
        return $this->__properties;
330
    }
331
332
    /**
333
     * Get all property types in array form
334
     * @return array<mixed>
335
     */
336
    public function getTypes(): array
337
    {
338
        return $this->__types;
339
    }
340
341
342
    /**
343
     * Get all properties in array form
344
     * @return array<mixed>
345
     */
346
    public function toArray(): array
347
    {
348
        $props = $this->getProperties();
349
        foreach ($props as $key => $value) {
350
            if ($value instanceof Model) {
351
                $props[$key] = $value->toArray();
352
            }
353
            if ($value instanceof Collection) {
354
                $props[$key] = $value->toArray();
355
            }
356
        }
357
        return $props;
358
    }
359
360
    /**
361
     * check if model loaded from db
362
     * @return bool
363
     */
364
    public function isLoaded(): bool
365
    {
366
        return $this->__meta['is_loaded'];
367
    }
368
369
    /**
370
     * call when model is loaded from database
371
     * @return Model
372
     */
373
    public function setLoaded(): Model
374
    {
375
        $this->__meta['is_loaded'] = true;
376
        $this->__meta['id_error'] = false;
377
        return $this;
378
    }
379
380
    /**
381
     * Get current table name of model
382
     * @return string
383
     */
384
    public function getName(): string
385
    {
386
        return $this->table;
387
    }
388
389
    /**
390
     * Get current model Id or UUID
391
     * @return mixed
392
     */
393
    public function getId(): mixed
394
    {
395
        return $this->__meta['id'];
396
    }
397
398
    /**
399
     * Check if model has id error
400
     * @return bool
401
     */
402
    public function hasIdError(): bool
403
    {
404
        return $this->__meta['id_error'];
405
    }
406
407
408
    /**
409
     * Save model to database
410
     * @return mixed
411
     */
412
    public function save(): mixed
413
    {
414
        Event::dispatch('__arca.model.save.' . $this->connection->getConnectionId(), [$this]);
415
416
        return $this->getId();
417
    }
418
419
    /**
420
     * Cleans up model internal state to be consistent after save
421
     * @return void
422
     */
423
    public function cleanModel()
424
    {
425
        $this->__props = $this->__properties;
426
        $this->__meta['has_foreign']['oto'] = false;
427
        $this->__meta['has_foreign']['otm'] = false;
428
        $this->__meta['has_foreign']['mtm'] = false;
429
        $this->__meta['id_error'] = false;
430
        $this->__meta['foreign_models']['otm'] = [];
431
        $this->__meta['foreign_models']['oto'] = [];
432
        $this->__meta['foreign_models']['mtm'] = [];
433
434
    }
435
436
    /**
437
     * Delete model data
438
     * @return void
439
     */
440
    public function delete(): void
441
    {
442
        Event::dispatch('__arca.model.delete.' . $this->connection->getConnectionId(), [$this]);
443
    }
444
445
    /**
446
     * Converts model into json object
447
     * @return string
448
     */
449
    public function toString(): string
450
    {
451
        return \Safe\json_encode($this->toArray());
452
    }
453
454
    /**
455
     * Converts model into json object
456
     * @return string
457
     */
458
    public function __toString(): string
459
    {
460
        return $this->toString();
461
    }
462
463
464
    /**
465
     * Function used to compare to models
466
     * @param Model $other
467
     * @return bool
468
     */
469
    public function equals(self $other): bool
470
    {
471
        return ($this->getId() === $other->getId() && $this->toString() === $other->toString());
472
    }
473
474
    /**
475
     * Check if model has any relations
476
     * @param string $type
477
     * @return bool
478
     */
479
    public function hasForeign(string $type): bool
480
    {
481
        return $this->__meta['has_foreign'][$type];
482
    }
483
484
    /**
485
     * returns all relational models
486
     * @param string $type
487
     * @return mixed[]
488
     */
489
    public function getForeignModels(string $type): array
490
    {
491
        return $this->__meta['foreign_models'][$type];
492
    }
493
}