Passed
Pull Request — 2.x (#231)
by
unknown
20:48
created

Database::upsert()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

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

214
            /** @scrutinizer ignore-call */ 
215
            $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...
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

214
            /** @scrutinizer ignore-call */ 
215
            $database->readDriver = $database->readDriver->withoutCache();
Loading history...
215
        }
216
217
        if ($this->driver instanceof Driver) {
218
            $database->driver = $database->readDriver === $database->driver
219
                ? ($database->readDriver = $database->driver->withoutCache())
220
                : $database->driver->withoutCache();
221
222
            return $database;
223
        }
224
225
        return $this;
226
    }
227
228
    /**
229
     * Shortcut to get table abstraction.
230
     *
231
     * @psalm-param non-empty-string $name Table name without prefix.
232
     */
233
    public function __get(string $name): TableInterface
234
    {
235
        return $this->table($name);
236
    }
237
}
238