Completed
Branch feature/pre-split (42159e)
by Anton
05:36
created

Driver::getFactory()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * Spiral Framework.
4
 *
5
 * @license   MIT
6
 * @author    Anton Titov (Wolfy-J)
7
 */
8
9
namespace Spiral\Database\Entities;
10
11
use Interop\Container\ContainerInterface;
12
use Psr\Log\LoggerInterface;
13
use Spiral\Core\Container;
14
use Spiral\Core\FactoryInterface;
15
use Spiral\Database\Builders\DeleteQuery;
16
use Spiral\Database\Builders\InsertQuery;
17
use Spiral\Database\Builders\SelectQuery;
18
use Spiral\Database\Builders\UpdateQuery;
19
use Spiral\Database\Schemas\Prototypes\AbstractTable;
20
21
/**
22
 * Driver abstraction is responsible for DBMS specific set of functions and used by Databases to
23
 * hide implementation specific functionality. Extends PDODriver and adds ability to create driver
24
 * specific query builders and schemas (basically operates like a factory).
25
 */
26
abstract class Driver extends PDODriver
27
{
28
    /**
29
     * Schema table class.
30
     */
31
    const TABLE_SCHEMA_CLASS = '';
32
33
    /**
34
     * Commander used to execute commands. :).
35
     */
36
    const COMMANDER = '';
37
38
    /**
39
     * Query compiler class.
40
     */
41
    const QUERY_COMPILER = '';
42
43
    /**
44
     * Transaction level (count of nested transactions). Not all drives can support nested
45
     * transactions.
46
     *
47
     * @var int
48
     */
49
    private $transactionLevel = 0;
50
51
    /**
52
     * Defines IoC scope for all driver specific builders.
53
     *
54
     * @var ContainerInterface
55
     */
56
    protected $container = null;
57
58
    /**
59
     * @param string             $name
60
     * @param array              $options
61
     * @param ContainerInterface $container Required to build instances of query builders and
62
     *                                      compilers. Also provides support for scope specific
63
     *                                      functionality like magic paginators and logs (yes, you
64
     *                                      can store LogsInterface in this container set profiling
65
     *                                      listener.
66
     */
67
    public function __construct(string $name, array $options, ContainerInterface $container = null)
68
    {
69
        parent::__construct($name, $options);
70
71
        //Factory with default fallback
72
        $this->container = $container ?? new Container();
73
    }
74
75
    /**
76
     * Check if table exists.
77
     *
78
     * @param string $name
79
     *
80
     * @return bool
81
     */
82
    abstract public function hasTable(string $name): bool;
83
84
    /**
85
     * Clean (truncate) specified driver table.
86
     *
87
     * @param string $table Table name with prefix included.
88
     */
89
    abstract public function truncateData(string $table);
90
91
    /**
92
     * Get every available table name as array.
93
     *
94
     * @return array
95
     */
96
    abstract public function tableNames(): array;
97
98
    /**
99
     * Get Driver specific AbstractTable implementation.
100
     *
101
     * @param string $table  Table name without prefix included.
102
     * @param string $prefix Database specific table prefix, this parameter is not required,
103
     *                       but if provided all
104
     *                       foreign keys will be created using it.
105
     *
106
     * @return AbstractTable
107
     */
108
    public function tableSchema(string $table, string $prefix = ''): AbstractTable
109
    {
110
        return $this->getFactory()->make(
111
            static::TABLE_SCHEMA_CLASS,
112
            ['driver' => $this, 'name' => $table, 'prefix' => $prefix]
113
        );
114
    }
115
116
    /**
117
     * Get instance of Driver specific QueryCompiler.
118
     *
119
     * @param string $prefix Database specific table prefix, used to quote table names and build
120
     *                       aliases.
121
     *
122
     * @return QueryCompiler
123
     */
124
    public function queryCompiler(string $prefix = ''): QueryCompiler
125
    {
126
        return $this->getFactory()->make(
127
            static::QUERY_COMPILER,
128
            ['driver' => $this, 'quoter' => new Quoter($this, $prefix)]
129
        );
130
    }
131
132
    /**
133
     * Get InsertQuery builder with driver specific query compiler.
134
     *
135
     * @param string $prefix     Database specific table prefix, used to quote table names and build
136
     *                           aliases.
137
     * @param array  $parameters Initial builder parameters.
138
     *
139
     * @return InsertQuery
140
     */
141 View Code Duplication
    public function insertBuilder(string $prefix, array $parameters = []): InsertQuery
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
142
    {
143
        return $this->getFactory()->make(
144
            InsertQuery::class,
145
            ['driver' => $this, 'compiler' => $this->queryCompiler($prefix)] + $parameters
146
        );
147
    }
148
149
    /**
150
     * Get SelectQuery builder with driver specific query compiler.
151
     *
152
     * @param string $prefix     Database specific table prefix, used to quote table names and build
153
     *                           aliases.
154
     * @param array  $parameters Initial builder parameters.
155
     *
156
     * @return SelectQuery
157
     */
158 View Code Duplication
    public function selectBuilder(string $prefix, array $parameters = []): SelectQuery
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
159
    {
160
        return $this->getFactory()->make(
161
            SelectQuery::class,
162
            ['driver' => $this, 'compiler' => $this->queryCompiler($prefix)] + $parameters
163
        );
164
    }
165
166
    /**
167
     * Get DeleteQuery builder with driver specific query compiler.
168
     *
169
     * @param string $prefix     Database specific table prefix, used to quote table names and build
170
     *                           aliases.
171
     * @param array  $parameters Initial builder parameters.
172
     *
173
     * @return DeleteQuery
174
     */
175 View Code Duplication
    public function deleteBuilder(string $prefix, array $parameters = []): DeleteQuery
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
    {
177
        return $this->getFactory()->make(
178
            DeleteQuery::class,
179
            ['driver' => $this, 'compiler' => $this->queryCompiler($prefix)] + $parameters
180
        );
181
    }
182
183
    /**
184
     * Get UpdateQuery builder with driver specific query compiler.
185
     *
186
     * @param string $prefix     Database specific table prefix, used to quote table names and build
187
     *                           aliases.
188
     * @param array  $parameters Initial builder parameters.
189
     *
190
     * @return UpdateQuery
191
     */
192 View Code Duplication
    public function updateBuilder(string $prefix, array $parameters = []): UpdateQuery
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
193
    {
194
        return $this->getFactory()->make(
195
            UpdateQuery::class,
196
            ['driver' => $this, 'compiler' => $this->queryCompiler($prefix)] + $parameters
197
        );
198
    }
199
200
    /**
201
     * Handler responsible for schema related operations. Handlers responsible for sync flow of
202
     * tables and columns, provide logger to aggregate all logger operations.
203
     *
204
     * @param LoggerInterface $logger
205
     *
206
     * @return AbstractHandler
207
     */
208
    abstract public function getHandler(LoggerInterface $logger = null): AbstractHandler;
209
210
    /**
211
     * Start SQL transaction with specified isolation level (not all DBMS support it). Nested
212
     * transactions are processed using savepoints.
213
     *
214
     * @link   http://en.wikipedia.org/wiki/Database_transaction
215
     * @link   http://en.wikipedia.org/wiki/Isolation_(database_systems)
216
     *
217
     * @param string $isolationLevel
218
     *
219
     * @return bool
220
     */
221
    public function beginTransaction(string $isolationLevel = null): bool
222
    {
223
        ++$this->transactionLevel;
224
225
        if ($this->transactionLevel == 1) {
226
            if (!empty($isolationLevel)) {
227
                $this->isolationLevel($isolationLevel);
228
            }
229
230
            if ($this->isProfiling()) {
231
                $this->logger()->info('Begin transaction');
232
            }
233
234
            return $this->getPDO()->beginTransaction();
235
        }
236
237
        $this->savepointCreate($this->transactionLevel);
238
239
        return true;
240
    }
241
242
    /**
243
     * Commit the active database transaction.
244
     *
245
     * @return bool
246
     */
247 View Code Duplication
    public function commitTransaction(): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
248
    {
249
        --$this->transactionLevel;
250
251
        if ($this->transactionLevel == 0) {
252
            if ($this->isProfiling()) {
253
                $this->logger()->info('Commit transaction');
254
            }
255
256
            return $this->getPDO()->commit();
257
        }
258
259
        $this->savepointRelease($this->transactionLevel + 1);
260
261
        return true;
262
    }
263
264
    /**
265
     * Rollback the active database transaction.
266
     *
267
     * @return bool
268
     */
269 View Code Duplication
    public function rollbackTransaction(): bool
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
270
    {
271
        --$this->transactionLevel;
272
273
        if ($this->transactionLevel == 0) {
274
            if ($this->isProfiling()) {
275
                $this->logger()->info('Rollback transaction');
276
            }
277
278
            return $this->getPDO()->rollBack();
279
        }
280
281
        $this->savepointRollback($this->transactionLevel + 1);
282
283
        return true;
284
    }
285
286
    /**
287
     * Get driver specific factory.
288
     *
289
     * @return FactoryInterface
290
     */
291
    protected function getFactory(): FactoryInterface
292
    {
293
        if ($this->container instanceof FactoryInterface) {
294
            return $this->container;
295
        }
296
297
        return $this->container->get(FactoryInterface::class);
298
    }
299
300
    /**
301
     * Set transaction isolation level, this feature may not be supported by specific database
302
     * driver.
303
     *
304
     * @param string $level
305
     */
306
    protected function isolationLevel(string $level)
307
    {
308
        if ($this->isProfiling()) {
309
            $this->logger()->info("Set transaction isolation level to '{$level}'");
310
        }
311
312
        if (!empty($level)) {
313
            $this->statement("SET TRANSACTION ISOLATION LEVEL {$level}");
314
        }
315
    }
316
317
    /**
318
     * Create nested transaction save point.
319
     *
320
     * @link http://en.wikipedia.org/wiki/Savepoint
321
     *
322
     * @param string $name Savepoint name/id, must not contain spaces and be valid database
323
     *                     identifier.
324
     */
325 View Code Duplication
    protected function savepointCreate(string $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
326
    {
327
        if ($this->isProfiling()) {
328
            $this->logger()->info("Creating savepoint '{$name}'");
329
        }
330
331
        $this->statement('SAVEPOINT ' . $this->identifier("SVP{$name}"));
332
    }
333
334
    /**
335
     * Commit/release savepoint.
336
     *
337
     * @link http://en.wikipedia.org/wiki/Savepoint
338
     *
339
     * @param string $name Savepoint name/id, must not contain spaces and be valid database
340
     *                     identifier.
341
     */
342 View Code Duplication
    protected function savepointRelease(string $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
343
    {
344
        if ($this->isProfiling()) {
345
            $this->logger()->info("Releasing savepoint '{$name}'");
346
        }
347
348
        $this->statement('RELEASE SAVEPOINT ' . $this->identifier("SVP{$name}"));
349
    }
350
351
    /**
352
     * Rollback savepoint.
353
     *
354
     * @link http://en.wikipedia.org/wiki/Savepoint
355
     *
356
     * @param string $name Savepoint name/id, must not contain spaces and be valid database
357
     *                     identifier.
358
     */
359 View Code Duplication
    protected function savepointRollback(string $name)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
360
    {
361
        if ($this->isProfiling()) {
362
            $this->logger()->info("Rolling back savepoint '{$name}'");
363
        }
364
        $this->statement('ROLLBACK TO SAVEPOINT ' . $this->identifier("SVP{$name}"));
365
    }
366
}
367