Test Failed
Push — main ( 1a7499...cd6f94 )
by Pranjal
02:51
created

Database::createRelationalTables()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 3
c 1
b 0
f 0
nc 2
nop 2
dl 0
loc 5
rs 10
1
<?php
2
declare(strict_types=1);
3
4
namespace Scrawler\Arca;
5
6
class Database
7
{
8
    public \Doctrine\DBAL\Connection $connection;
9
    public \Doctrine\DBAL\Platforms\AbstractPlatform $platform;
10
    public \Doctrine\DBAL\Schema\AbstractSchemaManager $manager;
11
    private Manager\TableManager $tableManager;
12
    private Manager\RecordManager $recordManager;
13
    private bool $isFroozen = false;
14
    private static $instance;
15
16
    public function __construct(array $connectionParams)
17
    {
18
        $this->connection = \Doctrine\DBAL\DriverManager::getConnection($connectionParams);
19
        $this->platform = $this->connection->getDatabasePlatform();
20
        $this->manager = $this->connection->createSchemaManager();
21
        $this->tableManager = new Manager\TableManager($this);
22
        $this->recordManager = new Manager\RecordManager($this);
23
        self::$instance = $this;
24
    }
25
26
    /**
27
     * Executes an SQL query and returns the number of row affected
28
     *
29
     * @param string $sql
30
     * @param array $params
31
     * @return integer
32
     */
33
    public function exec(string $sql, array $params=array()): int
34
    {
35
        return  $this->connection->executeStatement($sql, $params);
36
    }
37
38
    /**
39
     * Returns array of data from SQL select statement
40
     *
41
     * @param string $sql
42
     * @param array $params
43
     * @return array
44
     */
45
    public function getAll(string $sql, array $params=[]): array
46
    {
47
        return  $this->connection->executeQuery($sql, $params)->fetchAllAssociative();
48
    }
49
50
    /**
51
     * Creates model from name
52
     *
53
     * @param string $name
54
     * @return \Scrawler\Arca\Model
55
     */
56
    public function create(string $name) : Model
57
    {
58
        $model = new Model($name);
59
        return $model;
60
    }
61
62
    /**
63
     * Save model into database
64
     *
65
     * @param \Scrawler\Arca\Model $model
66
     * @return integer
67
     */
68
    public function save(\Scrawler\Arca\Model $model) : int
69
    {
70
        if ($model->hasForeign('oto')) {
71
            $this->saveForeignOto($model);
72
        }
73
        
74
        $this->createTables($model);
75
        $this->connection->beginTransaction();
76
77
        try {
78
            $id = $this->createRecords($model);
79
            $this->connection->commit();
80
        } catch (\Exception $e) {
81
            $this->connection->rollBack();
82
            throw $e;
83
        }
84
        
85
        if ($model->hasForeign('otm')) {
86
            $this->saveForeignOtm($model, $id);
87
        }
88
89
        if ($model->hasForeign('mtm')) {
90
            $this->saveForeignMtm($model, $id);
91
        }
92
93
        return $id;
94
    }
95
96
    private function createTables($model)
97
    {
98
        if (!$this->isFroozen) {
99
            $table = $this->tableManager->createTable($model);
100
            $this->tableManager->saveOrUpdateTable($model->getName(), $table);
101
        }
102
    }
103
104
    private function createRecords($model)
105
    {
106
        if ($model->isLoaded()) {
107
            return $this->recordManager->update($model);
108
        }
109
        
110
        return $this->recordManager->insert($model);
111
    }
112
113
    /**
114
     * Save One to One related model into database
115
     */
116
    private function saveForeignOto(\Scrawler\Arca\Model $model): void
117
    {
118
        foreach ($model->getForeignModels('oto') as $foreign) {
119
            $this->createTables($foreign);
120
        }
121
122
        $this->connection->beginTransaction();
123
        try {
124
            foreach ($model->getForeignModels('oto') as $foreign) {
125
                $id = $this->createRecords($foreign);
126
                $name = $foreign->getName().'_id';
127
                $model->$name = $id;
128
            }
129
            $this->connection->commit();
130
        } catch (\Exception $e) {
131
            $this->connection->rollBack();
132
            throw $e;
133
        }
134
    }
135
136
    /**
137
     * Save One to Many related model into database
138
     */
139
    private function saveForeignOtm(\Scrawler\Arca\Model $model, int $id): void
140
    {
141
        foreach ($model->getForeignModels('otm') as $foreigns) {
142
            foreach ($foreigns as $foreign) {
143
                $key = $model->getName().'_id';
144
                $foreign->$key = $id;
145
                $this->createTables($foreign);
146
            }
147
        }
148
        $this->connection->beginTransaction();
149
        try {
150
            foreach ($model->getForeignModels('otm') as $foreigns) {
151
                foreach ($foreigns as $foreign) {
152
                    $this->createRecords($foreign);
153
                }
154
            }
155
            $this->connection->commit();
156
        } catch (\Exception $e) {
157
            $this->connection->rollBack();
158
            throw $e;
159
        }
160
    }
161
162
    /**
163
     * Save Many to Many related model into database
164
     */
165
    private function saveForeignMtm(\Scrawler\Arca\Model $model, int $id): void
166
    {
167
        foreach ($model->getForeignModels('mtm') as $foreigns) {
168
            foreach ($foreigns as $foreign) {
169
                $model_id = $model->getName().'_id';
170
                $foreign_id = $foreign->getName().'_id';
171
                $relational_table = $this->create($model->getName().'_'.$foreign->getName());
172
                $relational_table->$model_id = 0;
173
                $relational_table->$foreign_id = 0;
174
                $this->createTables($relational_table);
175
                $this->createTables($foreign);
176
            }
177
        }
178
        $this->connection->beginTransaction();
179
        try {
180
            foreach ($model->getForeignModels('mtm') as $foreigns) {
181
                foreach ($foreigns as $foreign) {
182
                    $rel_id = $this->createRecords($foreign);
183
                    $model_id = $model->getName().'_id';
184
                    $foreign_id = $foreign->getName().'_id';
185
                    $relational_table = $this->create($model->getName().'_'.$foreign->getName());
186
                    $relational_table->$model_id = $id;
187
                    $relational_table->$foreign_id = $rel_id;
188
                    $this->createRecords($relational_table);
189
                }
190
            }
191
            $this->connection->commit();
192
        } catch (\Exception $e) {
193
            $this->connection->rollBack();
194
            throw $e;
195
        }
196
    }
197
198
    public function getTableManager()
199
    {
200
        return $this->tableManager;
201
    }
202
203
    /**
204
     * Delete record from database
205
     *
206
     * @param \Scrawler\Arca\Model $model
207
     * @return integer
208
     */
209
    public function delete(\Scrawler\Arca\Model $model) : int
210
    {
211
        return $this->recordManager->delete($model);
212
    }
213
214
    /**
215
     * Get single
216
     *
217
     * @param String $table
218
     * @param integer|null $id
219
     * @return mixed
220
     */
221
    public function get(String $table, int $id=null) : mixed
222
    {
223
        if (is_null($id)) {
224
            return $this->recordManager->getAll($table);
225
        }
226
227
        $model = $this->create($table);
228
        return $this->recordManager->getById($model, $id);
229
    }
230
231
    /**
232
     * Returns QueryBuilder to build query for finding data
233
     * Eg: db()->find('user')->where('active = 1')->get();
234
     *
235
     * @param string $name
236
     * @return QueryBuilder
237
     */
238
    public function find(string $name) : QueryBuilder
239
    {
240
        return $this->recordManager->find($name);
241
    }
242
243
244
    /**
245
     * Get current instance of database
246
     *
247
     * @return void
248
     */
249
    public static function getInstance() : Database
250
    {
251
        return self::$instance;
252
    }
253
254
    public function freeze() : void
255
    {
256
        $this->isFroozen = true;
257
    }
258
}
259