Passed
Push — main ( 7dc7cf...2fdc69 )
by Pranjal
02:49
created

Model::toArray()   A

Complexity

Conditions 4
Paths 5

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 4
eloc 7
c 3
b 0
f 0
nc 5
nop 0
dl 0
loc 12
rs 10

1 Method

Rating   Name   Duplication   Size   Complexity  
A Model::equals() 0 3 2
1
<?php
2
declare(strict_types=1);
3
namespace Scrawler\Arca;
4
5
use Scrawler\Arca\Connection;
6
use Doctrine\DBAL\Types\Type;
7
use Scrawler\Arca\Traits\Model\Serializable;
8
use Scrawler\Arca\Traits\Model\Stringable;
9
use Scrawler\Arca\Traits\Model\Iterator;
10
use Scrawler\Arca\Traits\Model\ArrayAccess;
11
use Scrawler\Arca\Traits\Model\Getter;
12
use Scrawler\Arca\Traits\Model\Setter;
13
14
15
/**
16
 * Model class that represents single record in database
17
 */
18
class Model implements \Stringable, \IteratorAggregate, \ArrayAccess
19
{
20
    use Iterator;
21
    use Stringable;
22
    use Serializable;
23
    use ArrayAccess;
24
    use Getter;
25
    use Setter;
26
27
    /**
28
     * Store all properties of model
29
     * @var array<string,array<string,mixed>>
30
     */
31
    private array $__properties = 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->__properties['all'] = [];
61
        $this->__properties['self'] = [];
62
        $this->__meta['is_loaded'] = false;
63
        $this->__meta['id_error'] = false;
64
        $this->__meta['foreign_models']['otm'] = null;
65
        $this->__meta['foreign_models']['oto'] = null;
66
        $this->__meta['foreign_models']['mtm'] = null;
67
        $this->__meta['id'] = 0;
68
    }
69
70
    /**
71
     * adds the key to properties
72
     * @param string $key
73
     * @param mixed $val
74
     * @return void
75
     */
76
    public function __set(string $key, mixed $val): void
77
    {
78
        $this->set($key, $val);
79
    }
80
81
    /**
82
     * Adds the key to properties
83
     * @param string $key
84
     * @param mixed $val
85
     * @return void
86
     */
87
    public function set(string $key, mixed $val): void
88
    {
89
        if ($key === 'id') {
90
            $this->__meta['id'] = $val;
91
            $this->__meta['id_error'] = true;
92
        }
93
94
        if (\Safe\preg_match('/[A-Z]/', $key)) {
95
            $parts = \Safe\preg_split('/(?=[A-Z])/', $key, -1, PREG_SPLIT_NO_EMPTY);
96
            if (strtolower($parts[0]) === 'own') {
97
                $this->__meta['foreign_models']['otm'] = $this->createCollection($this->__meta['foreign_models']['otm'],$val);
98
                $this->__properties['all'][$key] = $val;
99
                return;
100
            }
101
            if (strtolower($parts[0]) === 'shared') {
102
                $this->__meta['foreign_models']['mtm'] = $this->createCollection($this->__meta['foreign_models']['mtm'],$val);
103
                $this->__properties['all'][$key] = $val;
104
                return;
105
            }
106
        }
107
        if ($val instanceof Model) {
108
            if (isset($this->__properties['all'][$key . '_id'])) {
109
                unset($this->__properties['all'][$key . '_id']);
110
            }
111
            $this->__meta['foreign_models']['oto'] = $this->createCollection($this->__meta['foreign_models']['oto'], Collection::fromIterable([$val]));
112
            $this->__properties['all'][$key] = $val;
113
            return;
114
        }
115
116
        $type = $this->getDataType($val);
117
118
        if ($type === 'boolean') {
119
            ($val) ? $val = 1 : $val = 0;
120
        }
121
122
        if ($type === 'json_document') {
123
            $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

123
            $val = Type::getType('json_document')->convertToDatabaseValue($val, $this->connection->/** @scrutinizer ignore-call */ getDatabasePlatform());
Loading history...
124
        }
125
126
        $this->__properties['self'][$key] = $val;
127
        $this->__properties['all'][$key] = $val;
128
        $this->__properties['type'][$key] = $type;
129
130
    }
131
132
133
    /**
134
     * Get a key from properties, keys can be relational
135
     * like sharedList,ownList or foreign table
136
     * @param string $key
137
     * @return mixed
138
     */
139
    public function __get(string $key): mixed
140
    {
141
        return $this->get($key);
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
        if (\Safe\preg_match('/[A-Z]/', $key)) {
153
            $parts = \Safe\preg_split('/(?=[A-Z])/', $key, -1, PREG_SPLIT_NO_EMPTY);
154
            if (strtolower($parts[0]) == 'own') {
155
                if (strtolower($parts[2]) == 'list') {
156
                    $result = $this->connection->getRecordManager()->find(strtolower($parts[1]))->where($this->getName() . '_id = "' . $this->__meta['id'] . '"')->get();
157
                    $this->set($key, $result);
158
                    return $result;
159
                }
160
            }
161
            if (strtolower($parts[0]) == 'shared') {
162
                if (strtolower($parts[2]) == 'list') {
163
                    $rel_table = $this->connection->getTableManager()->tableExists($this->table . '_' . strtolower($parts[1])) ? $this->table . '_' . strtolower($parts[1]) : strtolower($parts[1]) . '_' . $this->table;
164
                    $relations = $this->connection->getRecordManager()->find($rel_table)->where($this->getName() . '_id = "' . $this->__meta['id'] . '"')->get();
165
                    $rel_ids = '';
166
                    foreach ($relations as $relation) {
167
                        $key = strtolower($parts[1]) . '_id';
168
                        $rel_ids .= "'" . $relation->$key . "',";
169
                    }
170
                    $rel_ids = substr($rel_ids, 0, -1);
171
                    $result = $this->connection->getRecordManager()->find(strtolower($parts[1]))->where('id IN (' . $rel_ids . ')')->get();
172
                    $this->set($key, $result);
173
                    return $result;
174
                }
175
            }
176
        }
177
178
        if (array_key_exists($key . '_id', $this->__properties['self'])) {
179
            $result = $this->connection->getRecordManager()->getById($key, $this->__properties['self'][$key . '_id']);
180
            $this->set($key, $result);
181
            return $result;
182
        }
183
184
        if (array_key_exists($key, $this->__properties['self'])) {
185
            $type = Type::getType($this->connection->getTableManager()->getTable($this->table)->getColumn($key)->getComment());
186
            $result = $type->convertToPHPValue($this->__properties['self'][$key], $this->connection->getDatabasePlatform());
187
            $this->set($key, $result);
188
            return $result;
189
        }
190
191
        throw new Exception\KeyNotFoundException();
192
    }
193
194
    /**
195
     * Eager Load relation variable
196
     * @param array<string> $relations
197
     * @return Model
198
     */
199
    public function with(array $relations): Model
200
    {
201
        foreach ($relations as $relation) {
202
            $this->get($relation);
203
        }
204
        return $this;
205
    }
206
207
    /**
208
     * Get the type of value
209
     * @param mixed $value
210
     * @return string
211
     */
212
    private function getDataType(mixed $value): string
213
    {
214
        $type = gettype($value);
215
216
        if ($type == 'array' || $type == 'object') {
217
            return 'json_document';
218
        }
219
220
        if ($type == 'string') {
221
            return 'text';
222
        }
223
224
        return $type;
225
    }
226
227
    /**
228
     * Check if array passed is instance of model
229
     * @param array<mixed>|Collection $models
230
     * @throws \Scrawler\Arca\Exception\InvalidModelException
231
     * @return Collection
232
     */
233
    private function createCollection(?Collection $collection, array|Collection $models): Collection
234
    {
235
        if(is_null($collection)){
236
            $collection = Collection::fromIterable([]);
237
        }
238
239
        if ($models instanceof Collection) {
0 ignored issues
show
introduced by
$models is never a sub-type of Scrawler\Arca\Collection.
Loading history...
240
            return $collection->merge($models);
241
        }
242
243
        if (count(array_filter($models, fn($d) => !$d instanceof Model)) > 0) {
244
            throw new Exception\InvalidModelException();
245
        }
246
247
        return $collection->merge(Collection::fromIterable($models));
248
    }
249
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
        $this->unset($key);
260
    }
261
262
    /**
263
     * Unset a property from model
264
     * @param string $key
265
     * @return void
266
     */
267
    public function unset(string $key): void
268
    {
269
        unset($this->__properties['self'][$key]);
270
        unset($this->__properties['all'][$key]);
271
        unset($this->__properties['type'][$key]);
272
    }
273
274
    /**
275
     * Check if property exists
276
     * @param string $key
277
     * @return bool
278
     */
279
    public function __isset(string $key): bool
280
    {
281
        return $this->isset($key);
282
    }
283
284
    /**
285
     * Check if property exists
286
     *
287
     * @param string $key
288
     * @return bool
289
     */
290
    public function isset(string $key): bool
291
    {
292
        return array_key_exists($key, $this->__properties['all']);
293
    }
294
295
296
    /**
297
     * check if model loaded from db
298
     * @return bool
299
     */
300
    public function isLoaded(): bool
301
    {
302
        return $this->__meta['is_loaded'];
303
    }
304
305
306
    /**
307
     * Check if model has id error
308
     * @return bool
309
     */
310
    public function hasIdError(): bool
311
    {
312
        return $this->__meta['id_error'];
313
    }
314
315
316
    /**
317
     * Save model to database
318
     * @return mixed
319
     */
320
    public function save(): mixed
321
    {
322
        Event::dispatch('__arca.model.save.' . $this->connection->getConnectionId(), [$this]);
323
324
        return $this->getId();
325
    }
326
327
    /**
328
     * Cleans up model internal state to be consistent after save
329
     * @return void
330
     */
331
    public function cleanModel()
332
    {
333
        $this->__properties['all'] = $this->__properties['self'];
334
        $this->__meta['id_error'] = false;
335
        $this->__meta['foreign_models']['otm'] = null;
336
        $this->__meta['foreign_models']['oto'] = null;
337
        $this->__meta['foreign_models']['mtm'] = null;
338
339
    }
340
341
    /**
342
     * Delete model data
343
     * @return void
344
     */
345
    public function delete(): void
346
    {
347
        Event::dispatch('__arca.model.delete.' . $this->connection->getConnectionId(), [$this]);
348
    }
349
350
    /**
351
     * Function used to compare to models
352
     * @param Model $other
353
     * @return bool
354
     */
355
    public function equals(self $other): bool
356
    {
357
        return ($this->getId() === $other->getId() && $this->toString() === $other->toString());
358
    }
359
360
    /**
361
     * Check if model has any relations
362
     * @param string $type
363
     * @return bool
364
     */
365
    public function hasForeign(string $type): bool
366
    {
367
        return !is_null($this->__meta['foreign_models'][$type]);
368
    }
369
370
}