AbstractRepository::update()   B
last analyzed

Complexity

Conditions 7
Paths 7

Size

Total Lines 26
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 14
c 1
b 0
f 0
nc 7
nop 2
dl 0
loc 26
rs 8.8333
1
<?php declare(strict_types=1);
2
3
namespace Janisbiz\LightOrm\Dms\MySQL\Repository;
4
5
use Janisbiz\LightOrm\Dms\MySQL\Connection\ConnectionInterface;
6
use Janisbiz\LightOrm\Dms\MySQL\Enum\CommandEnum;
7
use Janisbiz\LightOrm\Dms\MySQL\QueryBuilder\QueryBuilder;
8
use Janisbiz\LightOrm\Entity\EntityInterface;
9
use Janisbiz\LightOrm\Generator\Writer\WriterInterface;
10
use Janisbiz\LightOrm\QueryBuilder\QueryBuilderInterface as BaseQueryBuilderInterface;
11
use Janisbiz\LightOrm\Dms\MySQL\QueryBuilder\QueryBuilderInterface;
12
use Janisbiz\LightOrm\Repository\AbstractRepository as BaseAbstractRepository;
13
14
/**
15
 * @method ConnectionInterface getConnection()
16
 */
17
abstract class AbstractRepository extends BaseAbstractRepository
18
{
19
    /**
20
     * @param QueryBuilderInterface $queryBuilder
21
     * @param bool $toString
22
     *
23
     * @return string|EntityInterface
24
     * @throws RepositoryException|\Exception
25
     */
26
    protected function insert(QueryBuilderInterface $queryBuilder, bool $toString = false)
27
    {
28
        if (!($entity = $queryBuilder->getEntity())) {
29
            throw new RepositoryException(
30
                'Cannot perform insert on query without entity! Please create query builder with entity.'
31
            );
32
        }
33
34
        $this->addEntityInsertQuery($queryBuilder, $entity);
35
36
        if (true === $toString) {
37
            return $queryBuilder->toString();
38
        }
39
40
41
        $this->beginTransaction($connection = $this->getConnection());
42
43
        try {
44
            $this->prepareAndExecute($queryBuilder, $queryBuilder->bindValueData(), $connection);
45
        } catch (\Exception $e) {
46
            $this->rollBack($connection);
47
48
            throw $e;
49
        }
50
51
        if (!empty($primaryKeysAutoIncrement = $entity->primaryKeysAutoIncrement())) {
52
            $entityData = &$entity->data();
53
54
            foreach ($primaryKeysAutoIncrement as $primaryKeyAutoIncrement) {
55
                $entityData[$primaryKeyAutoIncrement] = (int) $connection->lastInsertId($entity::TABLE_NAME);
0 ignored issues
show
Bug introduced by
The method lastInsertId() does not exist on Janisbiz\LightOrm\Dms\My...ion\ConnectionInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Janisbiz\LightOrm\Dms\My...ion\ConnectionInterface. ( Ignorable by Annotation )

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

55
                $entityData[$primaryKeyAutoIncrement] = (int) $connection->/** @scrutinizer ignore-call */ lastInsertId($entity::TABLE_NAME);
Loading history...
56
            }
57
        }
58
59
        $this->commit($connection);
60
61
        return $entity;
62
    }
63
64
    /**
65
     * @param QueryBuilderInterface $queryBuilder
66
     * @param bool $toString
67
     *
68
     * @return string|EntityInterface
69
     */
70
    protected function insertIgnore(QueryBuilderInterface $queryBuilder, bool $toString = false)
71
    {
72
        return $this->insert($queryBuilder, $toString);
73
    }
74
75
    /**
76
     * @param QueryBuilderInterface $queryBuilder
77
     * @param bool $toString
78
     *
79
     * @return string|EntityInterface
80
     */
81
    protected function replace(QueryBuilderInterface $queryBuilder, bool $toString = false)
82
    {
83
        return $this->insert($queryBuilder, $toString);
84
    }
85
86
    /**
87
     * @param QueryBuilderInterface $queryBuilder
88
     * @param bool $toString
89
     *
90
     * @return null|string
91
     * @throws \Exception
92
     */
93
    protected function findOne(QueryBuilderInterface $queryBuilder, bool $toString = false)
94
    {
95
        if (true === $toString) {
96
            return $queryBuilder->toString();
97
        }
98
99
        try {
100
            $statement = $this->prepareAndExecute($queryBuilder, $queryBuilder->bindData());
101
            $statement->setFetchMode(
102
                \PDO::FETCH_CLASS,
103
                ($entity = $queryBuilder->getEntity()) ? \get_class($entity) : $this->getEntityClass(),
104
                [
105
                    false,
106
                ]
107
            );
108
        } catch (\Exception $e) {
109
            $this->rollback();
110
111
            throw $e;
112
        }
113
114
        return $statement->fetch() ?: null;
115
    }
116
117
    /**
118
     * @param QueryBuilderInterface $queryBuilder
119
     * @param bool $toString
120
     *
121
     * @return array|string
122
     * @throws \Exception
123
     */
124
    protected function find(QueryBuilderInterface $queryBuilder, bool $toString = false)
125
    {
126
        if (true === $toString) {
127
            return $queryBuilder->toString();
128
        }
129
130
        try {
131
            $statement = $this->prepareAndExecute($queryBuilder, $queryBuilder->bindData());
132
            $statement->setFetchMode(
133
                \PDO::FETCH_CLASS,
134
                ($entity = $queryBuilder->getEntity()) ? \get_class($entity) : $this->getEntityClass(),
135
                [
136
                    false,
137
                ]
138
            );
139
        } catch (\Exception $e) {
140
            $this->rollback();
141
142
            throw $e;
143
        }
144
145
        return $statement->fetchAll() ?: [];
146
    }
147
148
    /**
149
     * @param QueryBuilderInterface $queryBuilder
150
     * @param bool $toString
151
     *
152
     * @return null|bool|EntityInterface|string
153
     * @throws \Exception
154
     */
155
    protected function update(QueryBuilderInterface $queryBuilder, bool $toString = false)
156
    {
157
        if (($entity = $queryBuilder->getEntity()) && !$this->addEntityUpdateQuery($queryBuilder, $entity, $toString)) {
158
            return $entity;
159
        }
160
161
        if (true === $toString) {
162
            return $queryBuilder->toString();
163
        }
164
165
        $this->beginTransaction($connection = $this->getConnection());
166
167
        try {
168
            $connection->setSqlSafeUpdates();
169
170
            $this->prepareAndExecute($queryBuilder, $queryBuilder->bindData(), $connection);
171
172
            $connection->unsetSqlSafeUpdates();
173
        } catch (\Exception $e) {
174
            $this->rollBack($connection);
175
            $connection->unsetSqlSafeUpdates();
176
177
            throw $e;
178
        }
179
180
        return $this->commit($connection) ? ($entity ?: true) : false;
181
    }
182
183
    /**
184
     * @param QueryBuilderInterface $queryBuilder
185
     * @param bool $toString
186
     *
187
     * @return null|string|bool|EntityInterface
188
     */
189
    protected function updateIgnore(QueryBuilderInterface $queryBuilder, bool $toString = false)
190
    {
191
        return $this->update($queryBuilder, $toString);
192
    }
193
194
    /**
195
     * @param QueryBuilderInterface $queryBuilder
196
     * @param bool $toString
197
     *
198
     * @return bool|string
199
     * @throws \Exception
200
     */
201
    protected function delete(QueryBuilderInterface $queryBuilder, bool $toString = false)
202
    {
203
        if (($entity = $queryBuilder->getEntity()) && !$this->addEntityDeleteQuery($queryBuilder, $entity)) {
204
            return false;
205
        }
206
207
        if (true === $toString) {
208
            return $queryBuilder->toString();
209
        }
210
211
        $this->beginTransaction($connection = $this->getConnection());
212
213
        try {
214
            $connection->setSqlSafeUpdates();
215
216
            $this->prepareAndExecute($queryBuilder, $queryBuilder->bindData(), $connection);
217
218
            $connection->unsetSqlSafeUpdates();
219
        } catch (\Exception $e) {
220
            $this->rollBack($connection);
221
            $connection->unsetSqlSafeUpdates();
222
223
            throw $e;
224
        }
225
226
        return $this->commit($connection);
227
    }
228
229
    /**
230
     * @param QueryBuilderInterface $queryBuilder
231
     * @param bool $toString
232
     *
233
     * @return int
234
     * @throws RepositoryException|\Exception
235
     */
236
    protected function count(BaseQueryBuilderInterface $queryBuilder, bool $toString = false)
237
    {
238
        if (CommandEnum::SELECT !== $queryBuilder->commandData()) {
0 ignored issues
show
Bug introduced by
The method commandData() does not exist on Janisbiz\LightOrm\QueryB...r\QueryBuilderInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Janisbiz\LightOrm\QueryB...r\QueryBuilderInterface. ( Ignorable by Annotation )

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

238
        if (CommandEnum::SELECT !== $queryBuilder->/** @scrutinizer ignore-call */ commandData()) {
Loading history...
239
            throw new RepositoryException(\sprintf(
240
                'Command "%s" is not a valid command for count query! Use "%s" command to execute count query.',
241
                $queryBuilder->commandData(),
242
                CommandEnum::SELECT
243
            ));
244
        }
245
246
        $queryBuilderCount = $this
247
            ->createQueryBuilder()
248
            ->command($queryBuilder->commandData())
249
            ->column('COUNT(*)', true)
250
            ->table(\sprintf('(%s) AS total_count', $queryBuilder->buildQuery()), true)
251
            ->bind($queryBuilder->bindData())
0 ignored issues
show
Bug introduced by
The method bindData() does not exist on Janisbiz\LightOrm\QueryB...r\QueryBuilderInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Janisbiz\LightOrm\QueryB...r\QueryBuilderInterface. ( Ignorable by Annotation )

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

251
            ->bind($queryBuilder->/** @scrutinizer ignore-call */ bindData())
Loading history...
252
        ;
253
254
        if ($toString === true) {
255
            return $queryBuilderCount->toString();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $queryBuilderCount->toString() returns the type string which is incompatible with the documented return type integer.
Loading history...
256
        }
257
258
        $connection = $this->getConnection();
259
260
        try {
261
            $statement = $this->prepareAndExecute($queryBuilderCount, $queryBuilderCount->bindData(), $connection);
262
        } catch (\Exception $e) {
263
            $this->rollBack($connection);
264
265
            throw $e;
266
        }
267
268
        return $statement->fetchColumn(0);
269
    }
270
271
    /**
272
     * @param EntityInterface|null $entity
273
     *
274
     * @return QueryBuilderInterface
275
     */
276
    protected function createQueryBuilder(?EntityInterface $entity = null): BaseQueryBuilderInterface
277
    {
278
        return (new QueryBuilder($this->createRepositoryCallClosure(), $entity))
279
            ->column(\sprintf('%s.*', $this->getEntityClassConstant(WriterInterface::CLASS_CONSTANT_TABLE_NAME)))
280
            ->table($this->getEntityClassConstant(WriterInterface::CLASS_CONSTANT_TABLE_NAME))
281
        ;
282
    }
283
284
    /**
285
     * @param QueryBuilderInterface $queryBuilder
286
     * @param EntityInterface $entity
287
     *
288
     * @return $this
289
     */
290
    protected function addEntityInsertQuery(QueryBuilderInterface $queryBuilder, EntityInterface $entity)
291
    {
292
        $entityColumns = $entity->columns();
293
        $entityData = &$entity->data();
294
295
        foreach ($entityColumns as $column) {
296
            if (\array_key_exists($column, $entityData)) {
0 ignored issues
show
Bug introduced by
It seems like $entityData can also be of type double and integer and null and string; however, parameter $search of array_key_exists() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

296
            if (\array_key_exists($column, /** @scrutinizer ignore-type */ $entityData)) {
Loading history...
297
                $queryBuilder->value($column, $entityData[$column]);
298
            }
299
        }
300
301
        return $this;
302
    }
303
304
    /**
305
     * @param QueryBuilderInterface $queryBuilder
306
     * @param EntityInterface $entity
307
     * @param bool $toString
308
     *
309
     * @return bool
310
     */
311
    protected function addEntityUpdateQuery(
312
        QueryBuilderInterface $queryBuilder,
313
        EntityInterface $entity,
314
        bool $toString
315
    ) {
316
        $performUpdate = false;
317
        $entityData = &$entity->data();
318
        $entityDataOriginal = &$entity->dataOriginal();
319
320
        if (false === $entity->isNew() || true === $entity->isSaved()) {
321
            foreach ($entity->primaryKeys() as $primaryKey) {
322
                if (isset($entityDataOriginal[$primaryKey])) {
323
                    $queryBuilder->where(
324
                        \sprintf('%s.%s = :%s_WhereUpdate', $entity::TABLE_NAME, $primaryKey, $primaryKey),
325
                        [
326
                            \sprintf('%s_WhereUpdate', $primaryKey) => $entityDataOriginal[$primaryKey],
327
                        ]
328
                    );
329
                }
330
            }
331
        } else {
332
            return $performUpdate;
333
        }
334
335
        foreach ($entity->columns() as $column) {
336
            /** Updating only what is needed to update, for faster queries, skipping equal values */
337
            if (!\array_key_exists($column, $entityDataOriginal)
0 ignored issues
show
Bug introduced by
It seems like $entityDataOriginal can also be of type double and integer and null and string; however, parameter $search of array_key_exists() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

337
            if (!\array_key_exists($column, /** @scrutinizer ignore-type */ $entityDataOriginal)
Loading history...
338
                || (isset($entityDataOriginal[$column]) && $entityDataOriginal[$column] == $entityData[$column])
339
            ) {
340
                continue;
341
            } elseif (false === $performUpdate) {
342
                $performUpdate = true;
343
            }
344
345
            $queryBuilder->set(\sprintf('%s.%s', $entity::TABLE_NAME, $column), $entityData[$column]);
346
347
            /**
348
             * If we want to get query as astring, we don't perform actions on original data as update won't be
349
             * performed!
350
             */
351
            if (false === $toString) {
352
                $entityDataOriginal[$column] = $entityData[$column];
353
            }
354
        }
355
356
        return $performUpdate;
357
    }
358
359
    /**
360
     * @param QueryBuilderInterface $queryBuilder
361
     * @param EntityInterface $entity
362
     *
363
     * @return bool
364
     */
365
    protected function addEntityDeleteQuery(QueryBuilderInterface $queryBuilder, EntityInterface $entity)
366
    {
367
        $performDelete = false;
368
        $entityData = $entity->data();
369
370
        foreach ($entity->primaryKeys() as $primaryKey) {
371
            if (isset($entityData[$primaryKey]) && !\is_null($entityData[$primaryKey])) {
372
                if (false === $performDelete) {
373
                    $performDelete = true;
374
                }
375
376
                $primaryKeyNormalised = \sprintf(
377
                    '%s_Delete',
378
                    \implode(
379
                        '',
380
                        \array_map(
381
                            function ($columnPart) {
382
                                return \mb_convert_case($columnPart, MB_CASE_TITLE);
383
                            },
384
                            \array_merge(
385
                                \explode('.', $entity::TABLE_NAME),
386
                                [
387
                                    $primaryKey,
388
                                ]
389
                            )
390
                        )
391
                    )
392
                );
393
394
                $queryBuilder->where(
395
                    \sprintf('%s.%s = :%s', $entity::TABLE_NAME, $primaryKey, $primaryKeyNormalised),
396
                    [
397
                        $primaryKeyNormalised => $entityData[$primaryKey],
398
                    ]
399
                );
400
            }
401
        }
402
403
        return $performDelete;
404
    }
405
406
    /**
407
     * @param QueryBuilderInterface $queryBuilder
408
     * @param int $pageSize
409
     * @param int $currentPage
410
     *
411
     * @return $this
412
     */
413
    protected function addPaginateQuery(
414
        BaseQueryBuilderInterface $queryBuilder,
415
        int $pageSize,
416
        int $currentPage
417
    ): BaseAbstractRepository {
418
        $queryBuilder->limitWithOffset($pageSize, $pageSize * $currentPage - $pageSize);
0 ignored issues
show
Bug introduced by
The method limitWithOffset() does not exist on Janisbiz\LightOrm\QueryB...r\QueryBuilderInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Janisbiz\LightOrm\QueryB...r\QueryBuilderInterface. ( Ignorable by Annotation )

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

418
        $queryBuilder->/** @scrutinizer ignore-call */ 
419
                       limitWithOffset($pageSize, $pageSize * $currentPage - $pageSize);
Loading history...
419
420
        return $this;
421
    }
422
423
    /**
424
     * @param QueryBuilderInterface $queryBuilder
425
     * @param bool $toString
426
     *
427
     * @return EntityInterface[]
428
     */
429
    protected function getPaginateResult(BaseQueryBuilderInterface $queryBuilder, bool $toString = false): array
430
    {
431
        return $queryBuilder->find($toString);
0 ignored issues
show
Bug introduced by
The method find() does not exist on Janisbiz\LightOrm\QueryB...r\QueryBuilderInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to Janisbiz\LightOrm\QueryB...r\QueryBuilderInterface. ( Ignorable by Annotation )

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

431
        return $queryBuilder->/** @scrutinizer ignore-call */ find($toString);
Loading history...
432
    }
433
}
434