Completed
Push — develop ( d74711...190a84 )
by Neomerx
02:10
created

BaseRepository::setDatabaseSchema()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
1
<?php namespace Limoncello\Passport\Repositories;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Closure;
20
use DateTimeInterface;
21
use Doctrine\DBAL\Connection;
22
use Doctrine\DBAL\Query\QueryBuilder;
23
use Doctrine\DBAL\Types\Type;
24
use Limoncello\Passport\Contracts\Entities\DatabaseSchemaInterface;
25
use PDO;
26
27
/**
28
 * @package Limoncello\Passport
29
 */
30
abstract class BaseRepository
31
{
32
    /**
33
     * @return string
34
     */
35
    abstract protected function getTableNameForReading(): string;
36
37
    /**
38
     * @return string
39
     */
40
    abstract protected function getTableNameForWriting(): string;
41
42
    /**
43
     * @return string
44
     */
45
    abstract protected function getClassName(): string;
46
47
    /**
48
     * @return string
49
     */
50
    abstract protected function getPrimaryKeyName(): string;
51
52
    /**
53
     * @var Connection
54
     */
55
    private $connection;
56
57
    /**
58
     * @var DatabaseSchemaInterface
59
     */
60
    private $databaseSchema;
61
62
    /**
63
     * @param Closure $closure
64
     *
65
     * @return void
0 ignored issues
show
Documentation introduced by
Consider making the return type a bit more specific; maybe use NoType.

This check looks for the generic type array as a return type and suggests a more specific type. This type is inferred from the actual code.

Loading history...
66
     */
67 18
    public function inTransaction(Closure $closure): void
68
    {
69 18
        $connection = $this->getConnection();
70 18
        $connection->beginTransaction();
71
        try {
72 18
            $isOk = ($closure() === false ? null : true);
73 18
        } finally {
74 18
            isset($isOk) === true ? $connection->commit() : $connection->rollBack();
75
        }
76
    }
77
78
    /**
79
     * @return Connection
80
     */
81 26
    protected function getConnection(): Connection
82
    {
83 26
        return $this->connection;
84
    }
85
86
    /**
87
     * @param Connection $connection
88
     *
89
     * @return self
90
     */
91 32
    protected function setConnection(Connection $connection): self
92
    {
93 32
        $this->connection = $connection;
94
95 32
        return $this;
96
    }
97
98
    /**
99
     * @param array $columns
100
     *
101
     * @return array
102
     */
103 2
    protected function indexResources(array $columns = ['*']): array
104
    {
105 2
        $query = $this->getConnection()->createQueryBuilder();
106
107
        $statement = $query
108 2
            ->select($columns)
109 2
            ->from($this->getTableNameForReading())
110 2
            ->execute();
111
112 2
        $statement->setFetchMode(PDO::FETCH_CLASS, $this->getClassName());
113 2
        $result = $statement->fetchAll();
114
115 2
        return $result;
116
    }
117
118
    /**
119
     * @param array $values
120
     *
121
     * @return int
122
     */
123 25 View Code Duplication
    protected function createResource(array $values): int
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...
124
    {
125 25
        $query = $this->getConnection()->createQueryBuilder();
126
127 25
        $query->insert($this->getTableNameForWriting());
128 25
        foreach ($values as $key => $value) {
129 25
            $query->setValue($key, $this->createTypedParameter($query, $value));
130
        }
131
132 25
        $numberOfAdded = $query->execute();
133 25
        assert(is_int($numberOfAdded) === true);
134
135 25
        $lastInsertId = $this->getConnection()->lastInsertId();
136
137 25
        return $lastInsertId;
138
    }
139
140
    /**
141
     * @param string|int $identifier
142
     * @param array      $columns
143
     *
144
     * @return mixed
145
     */
146 22
    protected function readResource($identifier, array $columns = ['*'])
147
    {
148 22
        return $this->readResourceByColumn($identifier, $this->getPrimaryKeyName(), $columns);
149
    }
150
151
    /**
152
     * @param string|int $identifier
153
     * @param string     $column
154
     * @param array      $columns
155
     *
156
     * @return mixed
157
     */
158 22
    protected function readResourceByColumn($identifier, string $column, array $columns = ['*'])
159
    {
160 22
        $query = $this->getConnection()->createQueryBuilder();
161
162
        $statement = $query
163 22
            ->select($columns)
164 22
            ->from($this->getTableNameForReading())
165 22
            ->where($column . '=' . $this->createTypedParameter($query, $identifier))
166 22
            ->execute();
167
168 22
        $statement->setFetchMode(PDO::FETCH_CLASS, $this->getClassName());
169 22
        $result = $statement->fetch();
170
171 22
        return $result === false ? null : $result;
172
    }
173
174
    /**
175
     * @param string|int $identifier
176
     * @param array      $values
177
     *
178
     * @return int
179
     */
180 4 View Code Duplication
    protected function updateResource($identifier, array $values): int
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...
181
    {
182 4
        $query = $this->getConnection()->createQueryBuilder();
183
184
        $query
185 4
            ->update($this->getTableNameForWriting())
186 4
            ->where($this->getPrimaryKeyName() . '=' . $this->createTypedParameter($query, $identifier));
187 4
        foreach ($values as $key => $value) {
188 4
            $query->set($key, $this->createTypedParameter($query, $value));
189
        }
190
191 4
        $numberOfUpdated = $query->execute();
192 4
        assert(is_int($numberOfUpdated) === true);
193
194 4
        return $numberOfUpdated;
195
    }
196
197
    /**
198
     * @param string|int $identifier
199
     *
200
     * @return int
201
     */
202 4
    protected function deleteResource($identifier): int
203
    {
204 4
        $query = $this->getConnection()->createQueryBuilder();
205
206
        $query
207 4
            ->delete($this->getTableNameForWriting())
208 4
            ->where($this->getPrimaryKeyName() . '=' . $this->createTypedParameter($query, $identifier));
209
210 4
        $numberOfDeleted = $query->execute();
211 4
        assert(is_int($numberOfDeleted) === true);
212
213 4
        return $numberOfDeleted;
214
    }
215
216
    /**
217
     * @param string|int $primaryKey
218
     * @param array      $foreignKeys
219
     * @param string     $intTableName
220
     * @param string     $intPrimaryKeyName
221
     * @param string     $intForeignKeyName
222
     *
223
     * @return void
224
     */
225 18
    protected function createBelongsToManyRelationship(
226
        $primaryKey,
227
        array $foreignKeys,
228
        string $intTableName,
229
        string $intPrimaryKeyName,
230
        string $intForeignKeyName
231
    ): void {
232 18
        $connection = $this->getConnection();
233 18
        $query      = $connection->createQueryBuilder();
234
235 18
        $query->insert($intTableName)->values([$intPrimaryKeyName => '?', $intForeignKeyName => '?']);
236 18
        $statement = $connection->prepare($query->getSQL());
237
238 18
        foreach ($foreignKeys as $value) {
239 18
            $statement->bindValue(1, $primaryKey);
240 18
            $statement->bindValue(2, $value);
241 18
            $statement->execute();
242
        }
243
    }
244
245
    /**
246
     * @param string|int $identifier
247
     * @param string     $intTableName
248
     * @param string     $intPrimaryKeyName
249
     * @param string     $intForeignKeyName
250
     *
251
     * @return string[]
252
     */
253 20 View Code Duplication
    protected function readBelongsToManyRelationshipIdentifiers(
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...
254
        $identifier,
255
        string $intTableName,
256
        string $intPrimaryKeyName,
257
        string $intForeignKeyName
258
    ): array {
259 20
        $connection = $this->getConnection();
260 20
        $query      = $connection->createQueryBuilder();
261
262
        $query
263 20
            ->select($intForeignKeyName)
264 20
            ->from($intTableName)
265 20
            ->where($intPrimaryKeyName . '=' . $this->createTypedParameter($query, $identifier));
266
267 20
        $statement = $query->execute();
268 20
        $statement->setFetchMode(PDO::FETCH_NUM);
269 20
        $result = array_column($statement->fetchAll(), 0);
270
271 20
        return $result;
272
    }
273
274
    /**
275
     * @param string|int $identifier
276
     * @param string     $hasManyTableName
277
     * @param string     $hasManyColumn
278
     * @param string     $hasManyFkName
279
     *
280
     * @return string[]
281
     */
282 15 View Code Duplication
    protected function readHasManyRelationshipColumn(
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...
283
        $identifier,
284
        string $hasManyTableName,
285
        string $hasManyColumn,
286
        string $hasManyFkName
287
    ): array {
288 15
        $connection = $this->getConnection();
289 15
        $query      = $connection->createQueryBuilder();
290
291
        $query
292 15
            ->select($hasManyColumn)
293 15
            ->from($hasManyTableName)
294 15
            ->where($hasManyFkName . '=' . $this->createTypedParameter($query, $identifier));
295
296 15
        $statement = $query->execute();
297 15
        $statement->setFetchMode(PDO::FETCH_NUM);
298 15
        $result = array_column($statement->fetchAll(), 0);
299
300 15
        return $result;
301
    }
302
303
    /**
304
     * @param string     $intTableName
305
     * @param string     $intPrimaryKeyName
306
     * @param string|int $identifier
307
     *
308
     * @return int
309
     */
310 3
    protected function deleteBelongsToManyRelationshipIdentifiers(
311
        string $intTableName,
312
        string $intPrimaryKeyName,
313
        $identifier
314
    ): int {
315 3
        $connection = $this->getConnection();
316 3
        $query      = $connection->createQueryBuilder();
317
318
        $query
319 3
            ->delete($intTableName)
320 3
            ->where($intPrimaryKeyName . '=' . $this->createTypedParameter($query, $identifier));
321
322 3
        $numberOfDeleted = $query->execute();
323 3
        assert(is_int($numberOfDeleted) === true);
324
325 3
        return $numberOfDeleted;
326
    }
327
328
    /**
329
     * @param DateTimeInterface $dateTime
330
     *
331
     * @return string
332
     */
333 26
    protected function getDateTimeForDb(DateTimeInterface $dateTime): string
334
    {
335 26
        return Type::getType(Type::DATETIME)
336 26
            ->convertToDatabaseValue($dateTime, $this->getConnection()->getDatabasePlatform());
337
    }
338
339
    /**
340
     * @return DatabaseSchemaInterface
341
     */
342 28
    protected function getDatabaseSchema(): DatabaseSchemaInterface
343
    {
344 28
        return $this->databaseSchema;
345
    }
346
347
    /**
348
     * @param DatabaseSchemaInterface $databaseSchema
349
     *
350
     * @return self
351
     */
352 32
    protected function setDatabaseSchema(DatabaseSchemaInterface $databaseSchema): self
353
    {
354 32
        $this->databaseSchema = $databaseSchema;
355
356 32
        return $this;
357
    }
358
359
    /**
360
     * @param QueryBuilder $query
361
     * @param mixed        $value
362
     *
363
     * @return string
364
     *
365
     * @SuppressWarnings(PHPMD.ElseExpression)
366
     */
367 26
    protected function createTypedParameter(QueryBuilder $query, $value): string
368
    {
369 26
        if (is_bool($value) === true) {
370 24
            $type = PDO::PARAM_BOOL;
371 26
        } elseif (is_int($value) === true) {
372 12
            $type = PDO::PARAM_INT;
373 26
        } elseif ($value === null) {
374 25
            $type = PDO::PARAM_NULL;
375 26
        } elseif ($value instanceof DateTimeInterface) {
376 26
            $value = $this->getDateTimeForDb($value);
377 26
            $type = PDO::PARAM_STR;
378
        } else {
379 26
            $type = PDO::PARAM_STR;
380
        }
381
382 26
        return $query->createNamedParameter($value, $type);
383
    }
384
}
385