|
1
|
|
|
<?php |
|
2
|
|
|
|
|
3
|
|
|
namespace Pulsar\Driver; |
|
4
|
|
|
|
|
5
|
|
|
use Doctrine\DBAL\Connection; |
|
6
|
|
|
use Doctrine\DBAL\DBALException; |
|
7
|
|
|
use JAQB\Query\DeleteQuery; |
|
8
|
|
|
use JAQB\Query\InsertQuery; |
|
9
|
|
|
use JAQB\Query\SelectQuery; |
|
10
|
|
|
use JAQB\Query\UpdateQuery; |
|
11
|
|
|
use Pulsar\Exception\DriverException; |
|
12
|
|
|
use Pulsar\Model; |
|
13
|
|
|
use Pulsar\Query; |
|
14
|
|
|
|
|
15
|
|
|
class DbalDriver extends AbstractDriver |
|
16
|
|
|
{ |
|
17
|
|
|
/** |
|
18
|
|
|
* @var Connection |
|
19
|
|
|
*/ |
|
20
|
|
|
private $database; |
|
21
|
|
|
|
|
22
|
|
|
public function __construct(Connection $connection) |
|
23
|
|
|
{ |
|
24
|
|
|
$this->database = $connection; |
|
25
|
|
|
} |
|
26
|
|
|
|
|
27
|
|
|
public function getConnection($connection): Connection |
|
28
|
|
|
{ |
|
29
|
|
|
if ($connection) { |
|
30
|
|
|
throw new DriverException('Currently multiple connections are not supported'); |
|
31
|
|
|
} |
|
32
|
|
|
|
|
33
|
|
|
return $this->database; |
|
34
|
|
|
} |
|
35
|
|
|
|
|
36
|
|
|
public function createModel(Model $model, array $parameters) |
|
37
|
|
|
{ |
|
38
|
|
|
// build the SQL query |
|
39
|
|
|
$tablename = $model->getTablename(); |
|
40
|
|
|
$values = $this->serialize($parameters); |
|
41
|
|
|
$dbQuery = new InsertQuery(); |
|
42
|
|
|
$dbQuery->into($tablename)->values($values); |
|
43
|
|
|
|
|
44
|
|
|
// then execute the query through DBAL |
|
45
|
|
|
$db = $this->getConnection($model->getConnection()); |
|
46
|
|
|
|
|
47
|
|
|
try { |
|
48
|
|
|
$db->executeQuery($dbQuery->build(), $dbQuery->getValues()); |
|
49
|
|
|
|
|
50
|
|
|
return true; |
|
51
|
|
|
} catch (DBALException $original) { |
|
52
|
|
|
$e = new DriverException('An error occurred in the database driver when creating the '.$model::modelName().': '.$original->getMessage()); |
|
53
|
|
|
$e->setException($original); |
|
54
|
|
|
throw $e; |
|
55
|
|
|
} |
|
56
|
|
|
} |
|
57
|
|
|
|
|
58
|
|
|
public function getCreatedID(Model $model, $propertyName) |
|
59
|
|
|
{ |
|
60
|
|
|
try { |
|
61
|
|
|
$id = $this->getConnection($model->getConnection())->lastInsertId(); |
|
62
|
|
|
} catch (DBALException $original) { |
|
63
|
|
|
$e = new DriverException('An error occurred in the database driver when getting the ID of the new '.$model::modelName().': '.$original->getMessage()); |
|
64
|
|
|
$e->setException($original); |
|
65
|
|
|
throw $e; |
|
66
|
|
|
} |
|
67
|
|
|
|
|
68
|
|
|
return Model::cast($model::getProperty($propertyName), $id); |
|
|
|
|
|
|
69
|
|
|
} |
|
70
|
|
|
|
|
71
|
|
|
public function loadModel(Model $model) |
|
72
|
|
|
{ |
|
73
|
|
|
// build the SQL query |
|
74
|
|
|
$tablename = $model->getTablename(); |
|
75
|
|
|
$dbQuery = new SelectQuery(); |
|
76
|
|
|
$dbQuery->select('*') |
|
77
|
|
|
->from($tablename) |
|
78
|
|
|
->where($model->ids()); |
|
79
|
|
|
|
|
80
|
|
|
// then execute the query through DBAL |
|
81
|
|
|
$db = $this->getConnection($model->getConnection()); |
|
82
|
|
|
|
|
83
|
|
|
try { |
|
84
|
|
|
$row = $db->fetchAssoc($dbQuery->build(), $dbQuery->getValues()); |
|
85
|
|
|
} catch (DBALException $original) { |
|
86
|
|
|
$e = new DriverException('An error occurred in the database driver when loading an instance of '.$model::modelName().': '.$original->getMessage()); |
|
87
|
|
|
$e->setException($original); |
|
88
|
|
|
throw $e; |
|
89
|
|
|
} |
|
90
|
|
|
|
|
91
|
|
|
if (!is_array($row)) { |
|
92
|
|
|
return false; |
|
93
|
|
|
} |
|
94
|
|
|
|
|
95
|
|
|
return $row; |
|
96
|
|
|
} |
|
97
|
|
|
|
|
98
|
|
|
public function queryModels(Query $query) |
|
99
|
|
|
{ |
|
100
|
|
|
// build the SQL query |
|
101
|
|
|
$modelClass = $query->getModel(); |
|
102
|
|
|
$model = new $modelClass(); |
|
103
|
|
|
|
|
104
|
|
|
$tablename = $model->getTablename(); |
|
105
|
|
|
$dbQuery = $this->buildSelectQuery($query, $tablename); |
|
106
|
|
|
$dbQuery->select($this->prefixSelect('*', $tablename)) |
|
107
|
|
|
->limit($query->getLimit(), $query->getStart()) |
|
108
|
|
|
->orderBy($this->prefixSort($query->getSort(), $tablename)); |
|
109
|
|
|
|
|
110
|
|
|
// then execute the query through DBAL |
|
111
|
|
|
$db = $this->getConnection($model->getConnection()); |
|
112
|
|
|
|
|
113
|
|
|
try { |
|
114
|
|
|
return $db->fetchAll($dbQuery->build(), $dbQuery->getValues()); |
|
115
|
|
|
} catch (DBALException $original) { |
|
116
|
|
|
$e = new DriverException('An error occurred in the database driver while performing the '.$model::modelName().' query: '.$original->getMessage()); |
|
117
|
|
|
$e->setException($original); |
|
118
|
|
|
throw $e; |
|
119
|
|
|
} |
|
120
|
|
|
} |
|
121
|
|
|
|
|
122
|
|
|
public function updateModel(Model $model, array $parameters) |
|
123
|
|
|
{ |
|
124
|
|
|
if (0 == count($parameters)) { |
|
125
|
|
|
return true; |
|
126
|
|
|
} |
|
127
|
|
|
|
|
128
|
|
|
// build the SQL query |
|
129
|
|
|
$tablename = $model->getTablename(); |
|
130
|
|
|
$values = $this->serialize($parameters); |
|
131
|
|
|
$dbQuery = new UpdateQuery(); |
|
132
|
|
|
$dbQuery->table($tablename) |
|
133
|
|
|
->values($values) |
|
134
|
|
|
->where($model->ids()); |
|
135
|
|
|
|
|
136
|
|
|
// then execute the query through DBAL |
|
137
|
|
|
$db = $this->getConnection($model->getConnection()); |
|
138
|
|
|
|
|
139
|
|
|
try { |
|
140
|
|
|
$db->executeQuery($dbQuery->build(), $dbQuery->getValues()); |
|
141
|
|
|
|
|
142
|
|
|
return true; |
|
143
|
|
|
} catch (DBALException $original) { |
|
144
|
|
|
$e = new DriverException('An error occurred in the database driver when updating the '.$model::modelName().': '.$original->getMessage()); |
|
145
|
|
|
$e->setException($original); |
|
146
|
|
|
throw $e; |
|
147
|
|
|
} |
|
148
|
|
|
} |
|
149
|
|
|
|
|
150
|
|
|
public function deleteModel(Model $model) |
|
151
|
|
|
{ |
|
152
|
|
|
// build the SQL query |
|
153
|
|
|
$tablename = $model->getTablename(); |
|
154
|
|
|
$dbQuery = new DeleteQuery(); |
|
155
|
|
|
$dbQuery->from($tablename) |
|
156
|
|
|
->where($model->ids()); |
|
157
|
|
|
|
|
158
|
|
|
// then execute the query through DBAL |
|
159
|
|
|
$db = $this->getConnection($model->getConnection()); |
|
160
|
|
|
|
|
161
|
|
|
try { |
|
162
|
|
|
$db->executeQuery($dbQuery->build(), $dbQuery->getValues()); |
|
163
|
|
|
|
|
164
|
|
|
return true; |
|
165
|
|
|
} catch (DBALException $original) { |
|
166
|
|
|
$e = new DriverException('An error occurred in the database driver while deleting the '.$model::modelName().': '.$original->getMessage()); |
|
167
|
|
|
$e->setException($original); |
|
168
|
|
|
throw $e; |
|
169
|
|
|
} |
|
170
|
|
|
} |
|
171
|
|
|
|
|
172
|
|
|
public function count(Query $query) |
|
173
|
|
|
{ |
|
174
|
|
|
// build the SQL query |
|
175
|
|
|
$modelClass = $query->getModel(); |
|
176
|
|
|
$model = new $modelClass(); |
|
177
|
|
|
|
|
178
|
|
|
$tablename = $model->getTablename(); |
|
179
|
|
|
$dbQuery = $this->buildSelectQuery($query, $tablename); |
|
180
|
|
|
$dbQuery->count(); |
|
181
|
|
|
|
|
182
|
|
|
// then execute the query through DBAL |
|
183
|
|
|
return (int) $this->executeScalar($dbQuery, $model, 'count'); |
|
184
|
|
|
} |
|
185
|
|
|
|
|
186
|
|
|
public function sum(Query $query, $field) |
|
187
|
|
|
{ |
|
188
|
|
|
// build the SQL query |
|
189
|
|
|
$modelClass = $query->getModel(); |
|
190
|
|
|
$model = new $modelClass(); |
|
191
|
|
|
|
|
192
|
|
|
$tablename = $model->getTablename(); |
|
193
|
|
|
$dbQuery = $this->buildSelectQuery($query, $tablename); |
|
194
|
|
|
$dbQuery->sum($this->prefixColumn($field, $tablename)); |
|
195
|
|
|
|
|
196
|
|
|
// then execute the query through DBAL |
|
197
|
|
|
return (int) $this->executeScalar($dbQuery, $model, $field); |
|
198
|
|
|
} |
|
199
|
|
|
|
|
200
|
|
|
public function average(Query $query, $field) |
|
201
|
|
|
{ |
|
202
|
|
|
// build the SQL query |
|
203
|
|
|
$modelClass = $query->getModel(); |
|
204
|
|
|
$model = new $modelClass(); |
|
205
|
|
|
|
|
206
|
|
|
$tablename = $model->getTablename(); |
|
207
|
|
|
$dbQuery = $this->buildSelectQuery($query, $tablename); |
|
208
|
|
|
$dbQuery->average($this->prefixColumn($field, $tablename)); |
|
209
|
|
|
|
|
210
|
|
|
// then execute the query through DBAL |
|
211
|
|
|
return (int) $this->executeScalar($dbQuery, $model, $field); |
|
212
|
|
|
} |
|
213
|
|
|
|
|
214
|
|
|
public function max(Query $query, $field) |
|
215
|
|
|
{ |
|
216
|
|
|
// build the SQL query |
|
217
|
|
|
$modelClass = $query->getModel(); |
|
218
|
|
|
$model = new $modelClass(); |
|
219
|
|
|
|
|
220
|
|
|
$tablename = $model->getTablename(); |
|
221
|
|
|
$dbQuery = $this->buildSelectQuery($query, $tablename); |
|
222
|
|
|
$dbQuery->max($this->prefixColumn($field, $tablename)); |
|
223
|
|
|
|
|
224
|
|
|
// then execute the query through DBAL |
|
225
|
|
|
return (int) $this->executeScalar($dbQuery, $model, $field); |
|
226
|
|
|
} |
|
227
|
|
|
|
|
228
|
|
|
public function min(Query $query, $field) |
|
229
|
|
|
{ |
|
230
|
|
|
// build the SQL query |
|
231
|
|
|
$modelClass = $query->getModel(); |
|
232
|
|
|
$model = new $modelClass(); |
|
233
|
|
|
|
|
234
|
|
|
$tablename = $model->getTablename(); |
|
235
|
|
|
$dbQuery = $this->buildSelectQuery($query, $tablename); |
|
236
|
|
|
$dbQuery->min($this->prefixColumn($field, $tablename)); |
|
237
|
|
|
|
|
238
|
|
|
// then execute the query through DBAL |
|
239
|
|
|
return (int) $this->executeScalar($dbQuery, $model, $field); |
|
240
|
|
|
} |
|
241
|
|
|
|
|
242
|
|
|
////////////////////////// |
|
243
|
|
|
/// Helpers |
|
244
|
|
|
////////////////////////// |
|
245
|
|
|
|
|
246
|
|
|
/** |
|
247
|
|
|
* Builds a new select query. |
|
248
|
|
|
* |
|
249
|
|
|
* @param Query $query |
|
250
|
|
|
* @param string $tablename |
|
251
|
|
|
* |
|
252
|
|
|
* @return SelectQuery |
|
253
|
|
|
*/ |
|
254
|
|
|
private function buildSelectQuery(Query $query, string $tablename): SelectQuery |
|
255
|
|
|
{ |
|
256
|
|
|
$dbQuery = new SelectQuery(); |
|
257
|
|
|
$dbQuery->from($tablename) |
|
258
|
|
|
->where($this->prefixWhere($query->getWhere(), $tablename)); |
|
259
|
|
|
|
|
260
|
|
|
$this->addJoins($query, $tablename, $dbQuery); |
|
261
|
|
|
|
|
262
|
|
|
return $dbQuery; |
|
263
|
|
|
} |
|
264
|
|
|
|
|
265
|
|
|
/** |
|
266
|
|
|
* Executes a select query through DBAL and returns a scalar result. |
|
267
|
|
|
* |
|
268
|
|
|
* @param SelectQuery $query |
|
269
|
|
|
* @param Model $model |
|
270
|
|
|
* @param string $field |
|
271
|
|
|
* |
|
272
|
|
|
* @throws DriverException |
|
273
|
|
|
* |
|
274
|
|
|
* @return false|mixed |
|
275
|
|
|
*/ |
|
276
|
|
|
private function executeScalar(SelectQuery $query, Model $model, string $field) |
|
277
|
|
|
{ |
|
278
|
|
|
$db = $this->getConnection($model->getConnection()); |
|
279
|
|
|
|
|
280
|
|
|
try { |
|
281
|
|
|
return $db->fetchColumn($query->build(), $query->getValues()); |
|
282
|
|
|
} catch (DBALException $original) { |
|
283
|
|
|
$e = new DriverException('An error occurred in the database driver while getting the value of '.$model::modelName().'.'.$field.': '.$original->getMessage()); |
|
284
|
|
|
$e->setException($original); |
|
285
|
|
|
throw $e; |
|
286
|
|
|
} |
|
287
|
|
|
} |
|
288
|
|
|
|
|
289
|
|
|
public function startTransaction(?string $connection): void |
|
290
|
|
|
{ |
|
291
|
|
|
$this->getConnection($connection)->beginTransaction(); |
|
292
|
|
|
} |
|
293
|
|
|
|
|
294
|
|
|
public function rollBackTransaction(?string $connection): void |
|
295
|
|
|
{ |
|
296
|
|
|
$this->getConnection($connection)->rollBack(); |
|
297
|
|
|
} |
|
298
|
|
|
|
|
299
|
|
|
public function commitTransaction(?string $connection): void |
|
300
|
|
|
{ |
|
301
|
|
|
$this->getConnection($connection)->commit(); |
|
302
|
|
|
} |
|
303
|
|
|
} |
|
304
|
|
|
|
This check looks at variables that are passed out again to other methods.
If the outgoing method call has stricter type requirements than the method itself, an issue is raised.
An additional type check may prevent trouble.