Passed
Push — main ( 11edb5...4c2aa9 )
by Pranjal
02:14
created

Database::unfreeze()   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
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace Scrawler\Arca;
5
6
/**
7
 * 
8
 * Class that manages all interaction with database
9
 */
10
class Database
11
{
12
    /**
13
     * Store the instance of current connection
14
     * @var \Scrawler\Arca\Connection
15
     */
16
    private Connection $connection;
17
    /**
18
     * When $isFrozen is set to true tables are not updated/created
19
     * @var bool
20
     */
21
    private bool $isFroozen = false;
22
    
23
24
    /**
25
     * Create a new Database instance
26
     * @param \Scrawler\Arca\Connection $connection
27
     */
28
    public function __construct(Connection $connection)
29
    {
30
        $this->connection = $connection;
31
        $this->registerEvents();
32
33
    }
34
35
    /**
36
     * Register events
37
     * @return void
38
     */
39
    public function registerEvents(): void
40
    {
41
        Event::subscribeTo('system.model.save.'.$this->connection->getConnectionId(), function ($model) {
42
            return $this->save($model);
43
        });
44
        Event::subscribeTo('system.model.delete.'.$this->connection->getConnectionId(), function ($model) {
45
            return $this->delete($model);
46
        });
47
    }
48
49
    /**
50
     * Executes an SQL query and returns the number of row affected
51
     *
52
     * @param string $sql
53
     * @param array<mixed> $params
54
     * @return int|numeric-string
0 ignored issues
show
Documentation Bug introduced by
The doc comment int|numeric-string at position 2 could not be parsed: Unknown type name 'numeric-string' at position 2 in int|numeric-string.
Loading history...
55
     */
56
    public function exec(string $sql, array $params=array()): int|string
57
    {
58
        return  $this->connection->executeStatement($sql, $params);
0 ignored issues
show
Bug introduced by
The method executeStatement() 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

58
        return  $this->connection->/** @scrutinizer ignore-call */ executeStatement($sql, $params);
Loading history...
59
    }
60
61
    /**
62
     * Returns array of data from SQL select statement
63
     *
64
     * @param string $sql
65
     * @param array<mixed> $params
66
     * @return array<int,array<string,mixed>>
67
     */
68
    public function getAll(string $sql, array $params=[]): array
69
    {
70
        return  $this->connection->executeQuery($sql, $params)->fetchAllAssociative();
0 ignored issues
show
Bug introduced by
The method executeQuery() 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

70
        return  $this->connection->/** @scrutinizer ignore-call */ executeQuery($sql, $params)->fetchAllAssociative();
Loading history...
71
    }
72
73
    /**
74
     * Creates model from name
75
     *
76
     * @param string $name
77
     * @return \Scrawler\Arca\Model
78
     */
79
    public function create(string $name) : Model
80
    {
81
        return $this->connection->getModelManager()->create($name);
82
    }
83
84
    /**
85
     * Save model into database
86
     *
87
     * @param \Scrawler\Arca\Model $model
88
     * @return mixed returns int for id and string for uuid
89
     */
90
    public function save(\Scrawler\Arca\Model $model) : mixed
91
    {
92
        if ($model->hasForeign('oto')) {
93
            $this->saveForeignOto($model);
94
        }
95
        
96
        $this->createTables($model);
97
        $this->connection->beginTransaction();
0 ignored issues
show
Bug introduced by
The method beginTransaction() 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

97
        $this->connection->/** @scrutinizer ignore-call */ 
98
                           beginTransaction();
Loading history...
98
99
        try {
100
            $id = $this->createRecords($model);
101
            $model->set('id',$id);
102
            $model->setLoaded();
103
            $this->connection->commit();
0 ignored issues
show
Bug introduced by
The method commit() 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

103
            $this->connection->/** @scrutinizer ignore-call */ 
104
                               commit();
Loading history...
104
        } catch (\Exception $e) {
105
            $this->connection->rollBack();
0 ignored issues
show
Bug introduced by
The method rollBack() 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

105
            $this->connection->/** @scrutinizer ignore-call */ 
106
                               rollBack();
Loading history...
106
            throw $e;
107
        }
108
        
109
        if ($model->hasForeign('otm')) {
110
            $this->saveForeignOtm($model, $id);
111
        }
112
113
        if ($model->hasForeign('mtm')) {
114
            $this->saveForeignMtm($model, $id);
115
        }
116
117
        return $id;
118
    }
119
120
    /**
121
     * Create tables
122
     * @param \Scrawler\Arca\Model $model
123
     * @return void
124
     */
125
    private function createTables($model): void
126
    {
127
        if (!$this->isFroozen) {
128
            $table = $this->connection->getTableManager()->createTable($model);
129
            $this->connection->getTableManager()->saveOrUpdateTable($model->getName(), $table);
130
        }
131
    }
132
133
    /**
134
     * Create records
135
     * @param \Scrawler\Arca\Model $model
136
     * @return mixed
137
     */
138
    private function createRecords(Model $model) : mixed
139
    {
140
        if ($model->isLoaded()) {
141
            return $this->connection->getRecordManager()->update($model);
142
        }
143
        return $this->connection->getRecordManager()->insert($model);
144
    }
145
146
147
    /**
148
     * Save One to One related model into database
149
     * @param Model $model
150
     * @return void
151
     */
152
    private function saveForeignOto(\Scrawler\Arca\Model $model): void
153
    {
154
        foreach ($model->getForeignModels('oto') as $foreign) {
155
            $this->createTables($foreign);
156
        }
157
158
        $this->connection->beginTransaction();
159
        try {
160
            foreach ($model->getForeignModels('oto') as $foreign) {
161
                $id = $this->createRecords($foreign);
162
                $name = $foreign->getName().'_id';
163
                $model->$name = $id;
164
            }
165
            $this->connection->commit();
166
        } catch (\Exception $e) {
167
            $this->connection->rollBack();
168
            throw $e;
169
        }
170
    }
171
172
173
    /**
174
     * Save One to Many related model into database
175
     * @param Model $model
176
     * @param mixed $id
177
     * @return void
178
     */
179
    private function saveForeignOtm(\Scrawler\Arca\Model $model, mixed $id): void
180
    {
181
        foreach ($model->getForeignModels('otm') as $foreigns) {
182
            foreach ($foreigns as $foreign) {
183
                $key = $model->getName().'_id';
184
                $foreign->$key = $id;
185
                $this->createTables($foreign);
186
            }
187
        }
188
        $this->connection->beginTransaction();
189
        try {
190
            foreach ($model->getForeignModels('otm') as $foreigns) {
191
                foreach ($foreigns as $foreign) {
192
                    $this->createRecords($foreign);
193
                }
194
            }
195
            $this->connection->commit();
196
        } catch (\Exception $e) {
197
            $this->connection->rollBack();
198
            throw $e;
199
        }
200
    }
201
202
203
    /**
204
     * Save Many to Many related model into database
205
     * @param Model $model
206
     * @param mixed $id
207
     * @return void
208
     */
209
    private function saveForeignMtm(\Scrawler\Arca\Model $model, mixed $id): void
210
    {
211
        foreach ($model->getForeignModels('mtm') as $foreigns) {
212
            foreach ($foreigns as $foreign) {
213
                $model_id = $model->getName().'_id';
214
                $foreign_id = $foreign->getName().'_id';
215
                $relational_table = $this->create($model->getName().'_'.$foreign->getName());
216
                if ($this->isUsingUUID()) {
217
                    $relational_table->$model_id = "";
218
                    $relational_table->$foreign_id = "";
219
                } else {
220
                    $relational_table->$model_id = 0;
221
                    $relational_table->$foreign_id = 0;
222
                }
223
                $this->createTables($relational_table);
224
                $this->createTables($foreign);
225
            }
226
        }
227
        $this->connection->beginTransaction();
228
        try {
229
            foreach ($model->getForeignModels('mtm') as $foreigns) {
230
                foreach ($foreigns as $foreign) {
231
                    $rel_id = $this->createRecords($foreign);
232
                    $model_id = $model->getName().'_id';
233
                    $foreign_id = $foreign->getName().'_id';
234
                    $relational_table = $this->create($model->getName().'_'.$foreign->getName());
235
                    $relational_table->$model_id = $id;
236
                    $relational_table->$foreign_id = $rel_id;
237
                    $this->createRecords($relational_table);
238
                }
239
            }
240
            $this->connection->commit();
241
        } catch (\Exception $e) {
242
            $this->connection->rollBack();
243
            throw $e;
244
        }
245
    }
246
247
    /**
248
     * Delete record from database
249
     *
250
     * @param \Scrawler\Arca\Model $model
251
     * @return mixed
252
     */
253
    public function delete(\Scrawler\Arca\Model $model) : mixed
254
    {
255
        return $this->connection->getRecordManager()->delete($model);
256
    }
257
258
    /**
259
     * Get collection of all records from table
260
     * @param string $table
261
     * @return Collection
262
     */
263
    public function get(string $table) : Collection
264
    {
265
       
266
        return $this->connection->getRecordManager()->getAll($table);
267
    }
268
269
    /**
270
     * Get single record
271
     * @param string $table
272
     * @param mixed $id
273
     * @return Model
274
     */
275
    public function getOne(string $table, mixed $id) : Model|null
276
    {
277
        return $this->connection->getRecordManager()->getById($table, $id);
278
    }
279
280
    /**
281
     * Returns QueryBuilder to build query for finding data
282
     * Eg: db()->find('user')->where('active = 1')->get();
283
     *
284
     * @param string $name
285
     * @return QueryBuilder
286
     */
287
    public function find(string $name) : QueryBuilder
288
    {
289
        return $this->connection->getRecordManager()->find($name);
290
    }
291
292
    /**
293
     * Freezes table for production
294
     * @return void
295
     */
296
    public function freeze() : void
297
    {
298
        $this->isFroozen = true;
299
    }
300
301
    /**
302
     * Helper function to unfreeze table
303
     * @return void
304
     */
305
    public function unfreeze() : void
306
    {
307
        $this->isFroozen = false;
308
    }
309
310
    /**
311
     * Checks if database is currently using uuid rather than id
312
     * @return bool
313
     */
314
    public function isUsingUUID() : bool
315
    {
316
        return $this->connection->isUsingUUID();
317
    }
318
319
    /**
320
     * Returns the current connection
321
     * @return Connection
322
     */
323
    public function getConnection() : Connection
324
    {
325
        return $this->connection;
326
    }
327
328
    /**
329
     * Check if tables exist
330
     * @param array<int,string> $tables
331
     * @return bool
332
     */
333
    public function tablesExist(array $tables) : bool
334
    {
335
336
        return $this->connection->getSchemaManager()->tablesExist($tables);
337
    }
338
339
    /**
340
     * Check if table exists
341
     * @param string $table
342
     * @return bool
343
     */
344
    public function tableExists(string $table) : bool
345
    {
346
      
347
        return $this->connection->getSchemaManager()->tableExists($table);
348
    }
349
}
350