Passed
Push — main ( 9a75d9...911b53 )
by Pranjal
03:04
created

Model::setLoadedProperties()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 5
nc 2
nop 1
dl 0
loc 8
rs 10
c 0
b 0
f 0
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 types of properties
22
     * @var array<string,mixed>
23
     */
24
    private array $__types = array();
25
26
    /**
27
     * Store the table name of model
28
     * @var string
29
     */
30
    private string $table;
31
    /**
32
     * Store the metadata of model
33
     * @var array<string,mixed>
34
     */
35
    private array $__meta = [];
36
    /**
37
     * Store the connection
38
     * @var Connection
39
     */
40
    private Connection $connection;
41
42
43
    /**
44
     * Create a new model
45
     * @param string $name
46
     * @param Connection $connection
47
     */
48
    public function __construct(string $name,Connection $connection)
49
    {
50
51
        $this->table = $name;
52
        $this->connection = $connection;
53
        $this->__meta['has_foreign']['oto'] = false;
54
        $this->__meta['has_foreign']['otm'] = false;
55
        $this->__meta['has_foreign']['mtm'] = false;
56
        $this->__meta['is_loaded'] = false;
57
        $this->__meta['id_error'] = false;
58
        $this->__meta['foreign_models']['otm'] = [];
59
        $this->__meta['foreign_models']['oto'] = [];
60
        $this->__meta['foreign_models']['mtm'] = [];
61
        $this->__meta['id'] = 0;
62
    }
63
64
    /**
65
     * adds the key to properties
66
     * @param string $key
67
     * @param mixed $val
68
     * @return void
69
     */
70
    public function __set(string $key, mixed $val): void
71
    {
72
        $this->set($key, $val);
73
    }
74
75
    /**
76
     * Adds the key to properties
77
     * @param string $key
78
     * @param mixed $val
79
     * @return void
80
     */
81
    public function set(string $key, mixed $val): void
82
    {
83
        if ($key == 'id') {
84
            $this->__meta['id'] = $val;
85
            $this->__meta['id_error'] = true;
86
        }
87
        //bug: fix issue with bool storage
88
        if (gettype($val) == 'boolean') {
89
            ($val) ? $val = 1 : $val = 0;
90
        }
91
92
        if (\Safe\preg_match('/[A-Z]/', $key)) {
93
            $parts = \Safe\preg_split('/(?=[A-Z])/', $key, -1, PREG_SPLIT_NO_EMPTY);
94
            if (strtolower($parts[0]) == 'own') {
95
                if (gettype($val) == 'array') {
96
                    $this->checkModelArray($val);
97
                    array_push($this->__meta['foreign_models']['otm'], $val);
98
                    $this->__meta['has_foreign']['otm'] = true;
99
                }
100
                return;
101
            }
102
            if (strtolower($parts[0]) == 'shared') {
103
                if (gettype($val) == 'array') {
104
                    $this->checkModelArray($val);
105
                    array_push($this->__meta['foreign_models']['mtm'], $val);
106
                    $this->__meta['has_foreign']['mtm'] = true;
107
                }
108
                return;
109
            }
110
        }
111
        if ($val instanceof Model) {
112
            $this->__meta['has_foreign']['oto'] = true;
113
            array_push($this->__meta['foreign_models']['oto'], $val);
114
            return;
115
        }
116
117
        $type = gettype($val);
118
        if($type == 'array' || $type == 'object'){
119
            $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

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