Completed
Push — master ( 4174bd...a2828a )
by Jared
02:06
created

DatabaseDriver::rollBackTransaction()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 10
rs 9.9332
c 0
b 0
f 0
cc 3
nc 3
nop 1
1
<?php
2
3
/**
4
 * @author Jared King <[email protected]>
5
 *
6
 * @see http://jaredtking.com
7
 *
8
 * @copyright 2015 Jared King
9
 * @license MIT
10
 */
11
12
namespace Pulsar\Driver;
13
14
use JAQB\ConnectionManager;
15
use JAQB\Exception\JAQBException;
16
use JAQB\QueryBuilder;
17
use PDOException;
18
use PDOStatement;
19
use Pulsar\Exception\DriverException;
20
use Pulsar\Model;
21
use Pulsar\Query;
22
23
/**
24
 * Driver for storing models in a database using PDO.
25
 */
26
class DatabaseDriver extends AbstractDriver
27
{
28
    /**
29
     * @var ConnectionManager
30
     */
31
    private $connections;
32
33
    /**
34
     * @var QueryBuilder
35
     */
36
    private $connection;
37
38
    /**
39
     * @var int
40
     */
41
    private $transactionNestingLevel = 0;
42
43
    /**
44
     * Sets the connection manager.
45
     *
46
     * @param ConnectionManager $manager
47
     *
48
     * @return $this
49
     */
50
    public function setConnectionManager(ConnectionManager $manager)
51
    {
52
        $this->connections = $manager;
53
54
        return $this;
55
    }
56
57
    /**
58
     * Gets the connection manager.
59
     */
60
    public function getConnectionManager(): ConnectionManager
61
    {
62
        return $this->connections;
63
    }
64
65
    /**
66
     * Sets the default database connection.
67
     *
68
     * @param QueryBuilder $db
69
     *
70
     * @return $this
71
     */
72
    public function setConnection(QueryBuilder $db)
73
    {
74
        $this->connection = $db;
75
76
        return $this;
77
    }
78
79
    /**
80
     * Gets the database connection.
81
     *
82
     * @param string|null $id connection ID
83
     *
84
     * @throws DriverException when the connection has not been set yet
85
     */
86
    public function getConnection(?string $id): QueryBuilder
87
    {
88
        if ($this->connections) {
89
            try {
90
                if ($id) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $id of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
91
                    return $this->connections->get($id);
92
                } else {
93
                    return $this->connections->getDefault();
94
                }
95
            } catch (JAQBException | PDOException $e) {
96
                throw new DriverException($e->getMessage(), $e->getCode(), $e);
97
            }
98
        }
99
100
        if (!$this->connection) {
101
            throw new DriverException('The database driver has not been given a connection!');
102
        }
103
104
        return $this->connection;
105
    }
106
107
    public function createModel(Model $model, array $parameters)
108
    {
109
        $values = $this->serialize($parameters);
110
        $tablename = $model->getTablename();
111
        $db = $this->getConnection($model->getConnection());
112
113
        try {
114
            return $db->insert($values)
115
                    ->into($tablename)
116
                    ->execute() instanceof PDOStatement;
117
        } catch (PDOException $original) {
118
            $e = new DriverException('An error occurred in the database driver when creating the '.$model::modelName().': '.$original->getMessage());
119
            $e->setException($original);
120
            throw $e;
121
        }
122
    }
123
124
    public function getCreatedID(Model $model, $propertyName)
125
    {
126
        try {
127
            $id = $this->getConnection($model->getConnection())->lastInsertId();
128
        } catch (PDOException $original) {
129
            $e = new DriverException('An error occurred in the database driver when getting the ID of the new '.$model::modelName().': '.$original->getMessage());
130
            $e->setException($original);
131
            throw $e;
132
        }
133
134
        return Model::cast($model::getProperty($propertyName), $id);
0 ignored issues
show
Bug introduced by
It seems like $model::getProperty($propertyName) targeting Pulsar\Model::getProperty() can also be of type null; however, Pulsar\Model::cast() does only seem to accept array, maybe add an additional type check?

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.

Loading history...
135
    }
136
137
    public function loadModel(Model $model)
138
    {
139
        $tablename = $model->getTablename();
140
        $db = $this->getConnection($model->getConnection());
141
142
        try {
143
            $row = $db->select('*')
144
                ->from($tablename)
145
                ->where($model->ids())
146
                ->one();
147
        } catch (PDOException $original) {
148
            $e = new DriverException('An error occurred in the database driver when loading an instance of '.$model::modelName().': '.$original->getMessage());
149
            $e->setException($original);
150
            throw $e;
151
        }
152
153
        if (!is_array($row)) {
154
            return false;
155
        }
156
157
        return $row;
158
    }
159
160
    public function updateModel(Model $model, array $parameters)
161
    {
162
        if (0 == count($parameters)) {
163
            return true;
164
        }
165
166
        $values = $this->serialize($parameters);
167
        $tablename = $model->getTablename();
168
        $db = $this->getConnection($model->getConnection());
169
170
        try {
171
            return $db->update($tablename)
172
                    ->values($values)
173
                    ->where($model->ids())
174
                    ->execute() instanceof PDOStatement;
175
        } catch (PDOException $original) {
176
            $e = new DriverException('An error occurred in the database driver when updating the '.$model::modelName().': '.$original->getMessage());
177
            $e->setException($original);
178
            throw $e;
179
        }
180
    }
181
182
    public function deleteModel(Model $model)
183
    {
184
        $tablename = $model->getTablename();
185
        $db = $this->getConnection($model->getConnection());
186
187
        try {
188
            return $db->delete($tablename)
189
                    ->where($model->ids())
190
                    ->execute() instanceof PDOStatement;
191
        } catch (PDOException $original) {
192
            $e = new DriverException('An error occurred in the database driver while deleting the '.$model::modelName().': '.$original->getMessage());
193
            $e->setException($original);
194
            throw $e;
195
        }
196
    }
197
198
    public function queryModels(Query $query)
199
    {
200
        $modelClass = $query->getModel();
201
        $model = new $modelClass();
202
        $tablename = $model->getTablename();
203
204
        // build a DB query from the model query
205
        $dbQuery = $this->getConnection($model->getConnection())
206
            ->select($this->prefixSelect('*', $tablename))
207
            ->from($tablename)
208
            ->where($this->prefixWhere($query->getWhere(), $tablename))
209
            ->limit($query->getLimit(), $query->getStart())
210
            ->orderBy($this->prefixSort($query->getSort(), $tablename));
211
212
        // join conditions
213
        $this->addJoins($query, $tablename, $dbQuery);
214
215
        try {
216
            return $dbQuery->all();
217
        } catch (PDOException $original) {
218
            $e = new DriverException('An error occurred in the database driver while performing the '.$model::modelName().' query: '.$original->getMessage());
219
            $e->setException($original);
220
            throw $e;
221
        }
222
    }
223
224
    public function count(Query $query)
225
    {
226
        $modelClass = $query->getModel();
227
        $model = new $modelClass();
228
        $tablename = $model->getTablename();
229
        $db = $this->getConnection($model->getConnection());
230
231
        $dbQuery = $db->select()
232
            ->count()
233
            ->from($tablename)
234
            ->where($this->prefixWhere($query->getWhere(), $tablename));
235
        $this->addJoins($query, $tablename, $dbQuery);
236
237
        try {
238
            return (int) $dbQuery->scalar();
239
        } catch (PDOException $original) {
240
            $e = new DriverException('An error occurred in the database driver while getting the number of '.$model::modelName().' objects: '.$original->getMessage());
241
            $e->setException($original);
242
            throw $e;
243
        }
244
    }
245
246
    public function sum(Query $query, $field)
247
    {
248
        $modelClass = $query->getModel();
249
        $model = new $modelClass();
250
        $tablename = $model->getTablename();
251
        $db = $this->getConnection($model->getConnection());
252
253
        $dbQuery = $db->select()
254
            ->sum($this->prefixColumn($field, $tablename))
255
            ->from($tablename)
256
            ->where($this->prefixWhere($query->getWhere(), $tablename));
257
        $this->addJoins($query, $tablename, $dbQuery);
258
259
        try {
260
            return (int) $dbQuery->scalar();
261
        } catch (PDOException $original) {
262
            $e = new DriverException('An error occurred in the database driver while getting the sum of '.$model::modelName().' '.$field.': '.$original->getMessage());
263
            $e->setException($original);
264
            throw $e;
265
        }
266
    }
267
268
    public function average(Query $query, $field)
269
    {
270
        $modelClass = $query->getModel();
271
        $model = new $modelClass();
272
        $tablename = $model->getTablename();
273
        $db = $this->getConnection($model->getConnection());
274
275
        $dbQuery = $db->select()
276
            ->average($this->prefixColumn($field, $tablename))
277
            ->from($tablename)
278
            ->where($this->prefixWhere($query->getWhere(), $tablename));
279
        $this->addJoins($query, $tablename, $dbQuery);
280
281
        try {
282
            return (int) $dbQuery->scalar();
283
        } catch (PDOException $original) {
284
            $e = new DriverException('An error occurred in the database driver while getting the average of '.$model::modelName().' '.$field.': '.$original->getMessage());
285
            $e->setException($original);
286
            throw $e;
287
        }
288
    }
289
290
    public function max(Query $query, $field)
291
    {
292
        $modelClass = $query->getModel();
293
        $model = new $modelClass();
294
        $tablename = $model->getTablename();
295
        $db = $this->getConnection($model->getConnection());
296
297
        $dbQuery = $db->select()
298
            ->max($this->prefixColumn($field, $tablename))
299
            ->from($tablename)
300
            ->where($this->prefixWhere($query->getWhere(), $tablename));
301
        $this->addJoins($query, $tablename, $dbQuery);
302
303
        try {
304
            return (int) $dbQuery->scalar();
305
        } catch (PDOException $original) {
306
            $e = new DriverException('An error occurred in the database driver while getting the max of '.$model::modelName().' '.$field.': '.$original->getMessage());
307
            $e->setException($original);
308
            throw $e;
309
        }
310
    }
311
312
    public function min(Query $query, $field)
313
    {
314
        $modelClass = $query->getModel();
315
        $model = new $modelClass();
316
        $tablename = $model->getTablename();
317
        $db = $this->getConnection($model->getConnection());
318
319
        $dbQuery = $db->select()
320
            ->min($this->prefixColumn($field, $tablename))
321
            ->from($tablename)
322
            ->where($this->prefixWhere($query->getWhere(), $tablename));
323
        $this->addJoins($query, $tablename, $dbQuery);
324
325
        try {
326
            return (int) $dbQuery->scalar();
327
        } catch (PDOException $original) {
328
            $e = new DriverException('An error occurred in the database driver while getting the min of '.$model::modelName().' '.$field.': '.$original->getMessage());
329
            $e->setException($original);
330
            throw $e;
331
        }
332
    }
333
334
    public function startTransaction(?string $connection): void
335
    {
336
        if (0 == $this->transactionNestingLevel) {
337
            $this->getConnection($connection)->beginTransaction();
338
        }
339
340
        ++$this->transactionNestingLevel;
341
    }
342
343
    public function rollBackTransaction(?string $connection): void
344
    {
345
        if (0 == $this->transactionNestingLevel) {
346
            throw new DriverException('No active transaction');
347
        } elseif (1 == $this->transactionNestingLevel) {
348
            $this->getConnection($connection)->rollBack();
349
        }
350
351
        --$this->transactionNestingLevel;
352
    }
353
354
    public function commitTransaction(?string $connection): void
355
    {
356
        if (0 == $this->transactionNestingLevel) {
357
            throw new DriverException('No active transaction');
358
        } elseif (1 == $this->transactionNestingLevel) {
359
            $this->getConnection($connection)->commit();
360
        }
361
362
        --$this->transactionNestingLevel;
363
    }
364
}
365