Database::getTables()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 8
nc 3
nop 0
dl 0
loc 14
ccs 4
cts 4
cp 1
crap 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
/**
4
 * This file is part of Cycle ORM package.
5
 *
6
 * For the full copyright and license information, please view the LICENSE
7
 * file that was distributed with this source code.
8
 */
9
10
declare(strict_types=1);
11
12
namespace Cycle\Database;
13
14
use Cycle\Database\Driver\Driver;
15
use Cycle\Database\Driver\DriverInterface;
16
use Cycle\Database\Query\DeleteQuery;
17
use Cycle\Database\Query\InsertQuery;
18
use Cycle\Database\Query\SelectQuery;
19
use Cycle\Database\Query\UpdateQuery;
20
21
/**
22
 * Database class is high level abstraction at top of Driver. Databases usually linked to real
23
 * database or logical portion of database (filtered by prefix).
24
 */
25
final class Database implements DatabaseInterface
26
{
27
    // Isolation levels for transactions
28
    public const ISOLATION_SERIALIZABLE = DriverInterface::ISOLATION_SERIALIZABLE;
29
    public const ISOLATION_REPEATABLE_READ = DriverInterface::ISOLATION_REPEATABLE_READ;
30
    public const ISOLATION_READ_COMMITTED = DriverInterface::ISOLATION_READ_COMMITTED;
31
    public const ISOLATION_READ_UNCOMMITTED = DriverInterface::ISOLATION_READ_UNCOMMITTED;
32
33
    /**
34
     * @psalm-param non-empty-string $name Internal database name/id.
35
     *
36
     * @param string $prefix Default database table prefix, will be used for all table identifiers.
37
     * @param DriverInterface $driver Driver instance responsible for database connection.
38
     * @param DriverInterface|null $readDriver Read-only driver connection.
39
     */
40 3746
    public function __construct(
41
        private string $name,
42
        private string $prefix,
43
        private DriverInterface $driver,
44
        private ?DriverInterface $readDriver = null,
45
    ) {}
46 3746
47
    /**
48
     * @psalm-return non-empty-string
49
     */
50
    public function getName(): string
51
    {
52
        return $this->name;
53 138
    }
54
55 138
    /**
56
     * @psalm-return non-empty-string
57
     */
58
    public function getType(): string
59
    {
60
        return $this->getDriver(self::WRITE)->getType();
61 14
    }
62
63 14
    public function getDriver(int $type = DatabaseInterface::WRITE): DriverInterface
64
    {
65
        return $type === self::READ && $this->readDriver !== null ? $this->readDriver : $this->driver;
66
    }
67
68
    public function withPrefix(string $prefix, bool $add = true): DatabaseInterface
69 8
    {
70
        $database = clone $this;
71 8
72
        $add ? $database->prefix .= $prefix : $database->prefix = $prefix;
73
74 3738
        return $database;
75
    }
76 3738
77
    public function getPrefix(): string
78
    {
79 8
        return $this->prefix;
80
    }
81 8
82
    /**
83 8
     * @psalm-param non-empty-string $name
84
     */
85 8
    public function hasTable(string $name): bool
86
    {
87
        return $this->getDriver()->getSchemaHandler()->hasTable($this->prefix . $name);
88 1974
    }
89
90 1974
    /**
91
     * @return Table[]
92
     */
93
    public function getTables(): array
94
    {
95
        $schemaHandler = $this->getDriver(self::READ)->getSchemaHandler();
96 10
97
        $result = [];
98 10
        foreach ($schemaHandler->getTableNames($this->prefix) as $table) {
99
            $table = \str_contains($table, '.')
100
                ? \str_replace('.' . $this->prefix, '.', $table)
101
                : \substr($table, \strlen($this->prefix));
102
103
            $result[] = new Table($this, $table);
104 3636
        }
105
106 3636
        return $result;
107
    }
108 3636
109 3636
    /**
110 1826
     * @psalm-param non-empty-string $name
111 478
     */
112 1348
    public function table(string $name): Table
113
    {
114 1826
        return new Table($this, $name);
115
    }
116
117 3636
    /**
118
     * @psalm-param non-empty-string $query
119
     */
120
    public function execute(string $query, array $parameters = []): int
121
    {
122
        return $this->getDriver(self::WRITE)
123 2042
            ->execute($query, $parameters);
124
    }
125 2042
126
    /**
127
     * @psalm-param non-empty-string $query
128
     */
129
    public function query(string $query, array $parameters = []): StatementInterface
130
    {
131 16
        return $this->getDriver(self::READ)
132
            ->query($query, $parameters);
133 16
    }
134 16
135
    public function insert(?string $table = null): InsertQuery
136
    {
137
        return $this->getDriver(self::WRITE)
138
            ->getQueryBuilder()
139
            ->insertQuery($this->prefix, $table);
140 26
    }
141
142 26
    public function update(?string $table = null, array $values = [], array $where = []): UpdateQuery
143 26
    {
144
        return $this->getDriver(self::WRITE)
145
            ->getQueryBuilder()
146 464
            ->updateQuery($this->prefix, $table, $where, $values);
147
    }
148 464
149 464
    public function delete(?string $table = null, array $where = []): DeleteQuery
150 464
    {
151
        return $this->getDriver(self::WRITE)
152
            ->getQueryBuilder()
153 128
            ->deleteQuery($this->prefix, $table, $where);
154
    }
155 128
156 128
    public function select(mixed $columns = '*'): SelectQuery
157 128
    {
158
        $arguments = \func_get_args();
159
        if (isset($arguments[0]) && \is_array($arguments[0])) {
160 80
            //Can be required in some cases while collecting data from Table->select(), stupid bug.
161
            $arguments = $arguments[0];
162 80
        }
163 80
164 80
        return $this->getDriver(self::READ)
165
            ->getQueryBuilder()
166
            ->selectQuery($this->prefix, [], $arguments);
167 1888
    }
168
169 1888
    public function transaction(
170 1888
        callable $callback,
171
        ?string $isolationLevel = null,
172 20
    ): mixed {
173
        $this->begin($isolationLevel);
174
175 1888
        try {
176 1888
            $result = $callback($this);
177 1888
            $this->commit();
178
179
            return $result;
180 48
        } catch (\Throwable $e) {
181
            $this->rollback();
182
            throw $e;
183
        }
184 48
    }
185
186
    public function begin(?string $isolationLevel = null): bool
187 48
    {
188 40
        return $this->getDriver(self::WRITE)->beginTransaction($isolationLevel);
189
    }
190 40
191 8
    public function commit(): bool
192 8
    {
193 8
        return $this->getDriver(self::WRITE)->commitTransaction();
194
    }
195
196
    public function rollback(): bool
197 106
    {
198
        return $this->getDriver(self::WRITE)->rollbackTransaction();
199 106
    }
200
201
    public function withoutCache(): self
202 80
    {
203
        $database = clone $this;
204 80
205
        if ($this->readDriver instanceof Driver && $database->readDriver !== $database->driver) {
206
            $database->readDriver = $database->readDriver->withoutCache();
0 ignored issues
show
Bug introduced by
The method withoutCache() does not exist on Cycle\Database\Driver\DriverInterface. It seems like you code against a sub-type of Cycle\Database\Driver\DriverInterface such as Cycle\Database\Driver\Driver. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

206
            /** @scrutinizer ignore-call */ 
207
            $database->readDriver = $database->readDriver->withoutCache();
Loading history...
Bug introduced by
The method withoutCache() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

206
            /** @scrutinizer ignore-call */ 
207
            $database->readDriver = $database->readDriver->withoutCache();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
207 40
        }
208
209 40
        if ($this->driver instanceof Driver) {
210
            $database->driver = $database->readDriver === $database->driver
211
                ? ($database->readDriver = $database->driver->withoutCache())
212
                : $database->driver->withoutCache();
213
214
            return $database;
215
        }
216
217
        return $this;
218
    }
219
220
    /**
221
     * Shortcut to get table abstraction.
222
     *
223
     * @psalm-param non-empty-string $name Table name without prefix.
224
     */
225
    public function __get(string $name): TableInterface
226
    {
227
        return $this->table($name);
228
    }
229
}
230