Completed
Push — master ( 0f8412...adc2fd )
by Alex
02:27
created

PdoCrud.php (1 issue)

Labels
Severity
1
<?php
2
namespace Mezon\PdoCrud;
3
4
/**
5
 * Class PdoCrud
6
 *
7
 * @package Mezon
8
 * @subpackage PdoCrud
9
 * @author Dodonov A.A.
10
 * @version v.1.0 (2019/08/16)
11
 * @copyright Copyright (c) 2019, aeon.org
12
 */
13
14
// TODO mark all old methods as deprecated
15
// TODO use PHPUnit 8.5 in Travis
16
17
/**
18
 * Class provides simple CRUD operations
19
 */
20
class PdoCrud
21
{
22
23
    /**
24
     * PDO object
25
     *
26
     * @var \PDO
27
     */
28
    private $pdo = null;
29
30
    /**
31
     * PDO statement
32
     *
33
     * @var \PDOStatement
34
     */
35
    private $pdoStatement = null;
36
37
    /**
38
     * Method connects to the database
39
     *
40
     * @param array $connnectionData
41
     *            Connection settings
42
     * @codeCoverageIgnore
43
     */
44
    public function connect(array $connnectionData): void
45
    {
46
        // no need to test this single string. assume that PDO developers did it
47
        $this->pdo = new \PDO($connnectionData['dsn'], $connnectionData['user'], $connnectionData['password']);
48
49
        $this->query('SET NAMES utf8');
50
    }
51
52
    /**
53
     * Method handles request errors
54
     *
55
     * @param mixed $result
56
     *            Query result
57
     * @param string $query
58
     *            SQL Query
59
     * @codeCoverageIgnore
60
     */
61
    protected function processQueryError($result, string $query): void
62
    {
63
        if ($result === false) {
64
            $errorInfo = $this->pdo->errorInfo();
65
66
            throw (new \Exception($errorInfo[2] . ' in statement ' . $query));
67
        }
68
    }
69
70
    /**
71
     * Method sets safe query
72
     *
73
     * @param string $query
74
     *            safe query
75
     * @codeCoverageIgnore
76
     */
77
    public function prepare(string $query): void
78
    {
79
        $this->pdoStatement = $this->pdo->prepare($query, [
80
            \PDO::ATTR_CURSOR => \PDO::CURSOR_FWDONLY
81
        ]);
82
83
        if ($this->pdoStatement === false) {
84
            $errorInfo = $this->pdo->errorInfo();
85
            throw (new \Exception('Query "' . query . '" was not prepared. ' . $errorInfo[2], - 1));
0 ignored issues
show
The constant Mezon\PdoCrud\query was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
86
        }
87
    }
88
89
    /**
90
     * Method executes select query and fetches results
91
     *
92
     * @param array $data
93
     *            query data
94
     * @return array query result as an array of objects
95
     * @codeCoverageIgnore
96
     */
97
    public function execSelect(array $data): array
98
    {
99
        $this->pdoStatement->execute($data);
100
101
        return $this->pdoStatement->fetchAll(\PDO::FETCH_OBJ);
102
    }
103
104
    /**
105
     * Getting records
106
     *
107
     * @param string $fields
108
     *            List of fields
109
     * @param string $tableNames
110
     *            List of tables
111
     * @param string $where
112
     *            Condition
113
     * @param int $from
114
     *            First record in query
115
     * @param int $limit
116
     *            Count of records
117
     * @return array List of records
118
     * @deprecated since 2020-06-16
119
     */
120
    public function select(
121
        string $fields,
122
        string $tableNames,
123
        string $where = '1 = 1',
124
        int $from = 0,
125
        int $limit = 1000000): array
126
    {
127
        $query = "SELECT $fields FROM $tableNames WHERE $where LIMIT " . intval($from) . ' , ' . intval($limit);
128
129
        $result = $this->query($query);
130
131
        $this->processQueryError($result, $query);
132
133
        return $result->fetchAll(\PDO::FETCH_ASSOC);
134
    }
135
136
    /**
137
     * Method compiles set-query
138
     *
139
     * @param array $record
140
     *            Inserting record
141
     * @return string Compiled query string
142
     */
143
    protected function compileGetQuery(array $record): string
144
    {
145
        $setFieldsStatement = [];
146
147
        foreach ($record as $field => $value) {
148
            if (is_string($value) && strtoupper($value) === 'INC') {
149
                $setFieldsStatement[] = $field . ' = ' . $field . ' + 1';
150
            } elseif (is_string($value) && strtoupper($value) !== 'NOW()') {
151
                $setFieldsStatement[] = $field . ' = "' . $value . '"';
152
            } elseif ($value === null) {
153
                $setFieldsStatement[] = $field . ' = NULL';
154
            } else {
155
                $setFieldsStatement[] = $field . ' = ' . $value;
156
            }
157
        }
158
159
        return implode(' , ', $setFieldsStatement);
160
    }
161
162
    /**
163
     * Method compiles set-multyple-query
164
     *
165
     * @param array $records
166
     *            Inserting records
167
     * @return string Compiled query string
168
     */
169
    protected function setMultypleQuery(array $records): string
170
    {
171
        $query = '( ' . implode(' , ', array_keys($records[0])) . ' ) VALUES ';
172
173
        $values = [];
174
175
        foreach ($records as $record) {
176
            $values[] = "( '" . implode("' , '", array_values($record)) . "' )";
177
        }
178
179
        return $query . implode(' , ', $values);
180
    }
181
182
    /**
183
     * Updating records
184
     *
185
     * @param string $tableName
186
     *            Table name
187
     * @param array $record
188
     *            Updating records
189
     * @param string $where
190
     *            Condition
191
     * @param int $limit
192
     *            Liti for afffecting records
193
     * @return int Count of updated records
194
     */
195
    public function update(string $tableName, array $record, string $where, int $limit = 10000000): int
196
    {
197
        $query = 'UPDATE ' . $tableName . ' SET ' . $this->compileGetQuery($record) . ' WHERE ' . $where . ' LIMIT ' .
198
            $limit;
199
200
        $result = $this->query($query);
201
202
        $this->processQueryError($result, $query);
203
204
        return $result->rowCount();
205
    }
206
207
    /**
208
     * Deleting records
209
     *
210
     * @param string $tableName
211
     *            Table name
212
     * @param string $where
213
     *            Condition
214
     * @param int $limit
215
     *            Liti for afffecting records
216
     * @return int Count of deleted records
217
     */
218
    public function delete($tableName, $where, $limit = 10000000): int
219
    {
220
        $query = 'DELETE FROM ' . $tableName . ' WHERE ' . $where . ' LIMIT ' . intval($limit);
221
222
        $result = $this->query($query);
223
224
        $this->processQueryError($result, $query);
225
226
        return $result->rowCount();
227
    }
228
229
    /**
230
     * Method compiles lock queries
231
     *
232
     * @param array $tables
233
     *            List of tables
234
     * @param array $modes
235
     *            List of lock modes
236
     * @return string Query
237
     */
238
    protected function lockQuery(array $tables, array $modes): string
239
    {
240
        $query = [];
241
242
        foreach ($tables as $i => $table) {
243
            $query[] = $table . ' ' . $modes[$i];
244
        }
245
246
        return 'LOCK TABLES ' . implode(' , ', $query);
247
    }
248
249
    /**
250
     * Method locks tables
251
     *
252
     * @param array $tables
253
     *            List of tables
254
     * @param array $modes
255
     *            List of lock modes
256
     */
257
    public function lock(array $tables, array $modes): void
258
    {
259
        $query = $this->lockQuery($tables, $modes);
260
261
        $result = $this->query($query);
262
263
        $this->processQueryError($result, $query);
264
    }
265
266
    /**
267
     * Method unlocks locked tables
268
     */
269
    public function unlock(): void
270
    {
271
        $result = $this->query('UNLOCK TABLES');
272
273
        $this->processQueryError($result, 'UNLOCK TABLES');
274
    }
275
276
    /**
277
     * Method starts transaction
278
     */
279
    public function startTransaction(): void
280
    {
281
        // setting autocommit off
282
        $result = $this->query('SET AUTOCOMMIT = 0');
283
284
        $this->processQueryError($result, 'SET AUTOCOMMIT = 0');
285
286
        // starting transaction
287
        $result = $this->query('START TRANSACTION');
288
289
        $this->processQueryError($result, 'START TRANSACTION');
290
    }
291
292
    /**
293
     * Commiting transaction
294
     */
295
    public function commit(): void
296
    {
297
        // commit transaction
298
        $result = $this->query('COMMIT');
299
300
        $this->processQueryError($result, 'COMMIT');
301
302
        // setting autocommit on
303
        $result = $this->query('SET AUTOCOMMIT = 1');
304
305
        $this->processQueryError($result, 'SET AUTOCOMMIT = 1');
306
    }
307
308
    /**
309
     * Rollback transaction
310
     */
311
    public function rollback(): void
312
    {
313
        // rollback transaction
314
        $result = $this->query('ROLLBACK');
315
316
        $this->processQueryError($result, 'ROLLBACK');
317
    }
318
319
    /**
320
     * Method executes query
321
     *
322
     * @param string $query
323
     *            Query
324
     * @return mixed Query execution result
325
     */
326
    public function query(string $query)
327
    {
328
        // @codeCoverageIgnoreStart
329
        return $this->pdo->query($query);
330
        // @codeCoverageIgnoreEnd
331
    }
332
333
    /**
334
     * Method returns id of the last inserted record
335
     *
336
     * @return int id of the last inserted record
337
     */
338
    public function lastInsertId(): int
339
    {
340
        // @codeCoverageIgnoreStart
341
        return (int) $this->pdo->lastInsertId();
342
        // @codeCoverageIgnoreEnd
343
    }
344
345
    /**
346
     * Method inserts record
347
     *
348
     * @param string $tableName
349
     *            Table name
350
     * @param array $record
351
     *            Inserting record
352
     * @return int New record's id
353
     */
354
    public function insert(string $tableName, array $record): int
355
    {
356
        $query = 'INSERT ' . $tableName . ' SET ' . $this->compileGetQuery($record);
357
358
        $result = $this->query($query);
359
360
        $this->processQueryError($result, $query);
361
362
        return $this->lastInsertId();
363
    }
364
365
    /**
366
     * Method inserts record
367
     *
368
     * @param string $tableName
369
     *            Table name
370
     * @param array $records
371
     *            Inserting records
372
     * @return int New record's id
373
     */
374
    public function insertMultyple(string $tableName, array $records): int
375
    {
376
        $query = 'INSERT INTO ' . $tableName . ' ' . $this->setMultypleQuery($records) . ';';
377
378
        $result = $this->query($query);
379
380
        $this->processQueryError($result, $query);
381
382
        return 0;
383
    }
384
385
    /**
386
     * Method destroys connection
387
     */
388
    public function __destruct()
389
    {
390
        $this->pdo = null;
391
392
        unset($this->pdo);
393
    }
394
}
395