Completed
Push — master ( 46b6cb...4549c0 )
by Dominik
01:55
created

AbstractDoctrineRepository   A

Complexity

Total Complexity 20

Size/Duplication

Total Lines 213
Duplicated Lines 7.51 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 0
Metric Value
wmc 20
lcom 1
cbo 8
dl 16
loc 213
rs 10
c 0
b 0
f 0

9 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 9 1
B find() 8 27 3
B findOneBy() 8 25 2
B findBy() 0 35 5
A getFindByQueryBuilder() 0 12 2
B persist() 0 22 5
A remove() 0 11 1
A fromRow() 0 7 1
getTable() 0 1 ?

How to fix   Duplicated Code   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Chubbyphp\Model\Doctrine\DBAL;
6
7
use Chubbyphp\Model\Cache\ModelCache;
8
use Chubbyphp\Model\Cache\ModelCacheInterface;
9
use Chubbyphp\Model\ModelInterface;
10
use Chubbyphp\Model\RepositoryInterface;
11
use Doctrine\DBAL\Connection;
12
use Doctrine\DBAL\Query\QueryBuilder;
13
use Psr\Log\LoggerInterface;
14
use Psr\Log\NullLogger;
15
16
abstract class AbstractDoctrineRepository implements RepositoryInterface
17
{
18
    /**
19
     * @var Connection
20
     */
21
    private $connection;
22
23
    /**
24
     * @var ModelCacheInterface
25
     */
26
    private $cache;
27
28
    /**
29
     * @var LoggerInterface
30
     */
31
    private $logger;
32
33
    /**
34
     * @param Connection               $connection
35
     * @param ModelCacheInterface|null $cache
36
     * @param LoggerInterface|null     $logger
37
     */
38
    public function __construct(
39
        Connection $connection,
40
        ModelCacheInterface $cache = null,
41
        LoggerInterface $logger = null
42
    ) {
43
        $this->connection = $connection;
44
        $this->cache = $cache ?? new ModelCache();
45
        $this->logger = $logger ?? new NullLogger();
46
    }
47
48
    /**
49
     * @param string $id
50
     *
51
     * @return ModelInterface|null
52
     */
53
    public function find(string $id)
54
    {
55
        $modelClass = $this->getModelClass();
56
57
        $this->logger->info('model: find model {model} with id {id}', ['model' => $modelClass, 'id' => $id]);
58
59
        if ($this->cache->has($id)) {
60
            return $this->fromRow($this->cache->get($id));
61
        }
62
63
        $qb = $this->connection->createQueryBuilder();
64
        $qb->select('*')->from($this->getTable())->where($qb->expr()->eq('id', ':id'))->setParameter('id', $id);
65
66
        $row = $qb->execute()->fetch(\PDO::FETCH_ASSOC);
67 View Code Duplication
        if (false === $row) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
68
            $this->logger->warning(
69
                'model: model {model} with id {id} not found',
70
                ['model' => $modelClass, 'id' => $id]
71
            );
72
73
            return null;
74
        }
75
76
        $this->cache->set($row['id'], $row);
77
78
        return $this->fromRow($row);
79
    }
80
81
    /**
82
     * @param array $criteria
83
     *
84
     * @return null|ModelInterface
85
     */
86
    public function findOneBy(array $criteria)
87
    {
88
        $modelClass = $this->getModelClass();
89
90
        $this->logger->info(
91
            'model: find model {model} with criteria {criteria}',
92
            ['model' => $modelClass, 'criteria' => $criteria]
93
        );
94
95
        $qb = $this->getFindByQueryBuilder($criteria)->setMaxResults(1);
96
97
        $row = $qb->execute()->fetch(\PDO::FETCH_ASSOC);
98 View Code Duplication
        if (false === $row) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
99
            $this->logger->warning(
100
                'model: model {model} with criteria {criteria} not found',
101
                ['model' => $modelClass, 'criteria' => $criteria]
102
            );
103
104
            return null;
105
        }
106
107
        $this->cache->set($row['id'], $row);
108
109
        return $this->fromRow($row);
110
    }
111
112
    /**
113
     * @param array $criteria
114
     *
115
     * @return ModelInterface[]|array
116
     */
117
    public function findBy(array $criteria, array $orderBy = null, int $limit = null, int $offset = null): array
118
    {
119
        $modelClass = $this->getModelClass();
120
121
        $this->logger->info(
122
            'model: find model {model} with criteria {criteria}',
123
            ['model' => $modelClass, 'criteria' => $criteria]
124
        );
125
126
        $qb = $this
127
            ->getFindByQueryBuilder($criteria)
128
            ->setFirstResult($offset)
129
            ->setMaxResults($limit)
130
        ;
131
132
        if (null !== $orderBy) {
133
            foreach ($orderBy as $field => $direction) {
134
                $qb->addOrderBy($field, $direction);
135
            }
136
        }
137
138
        $rows = $qb->execute()->fetchAll(\PDO::FETCH_ASSOC);
139
140
        if ([] === $rows) {
141
            return [];
142
        }
143
144
        $models = [];
145
        foreach ($rows as $row) {
146
            $this->cache->set($row['id'], $row);
147
            $models[] = $this->fromRow($row);
148
        }
149
150
        return $models;
151
    }
152
153
    /**
154
     * @param array $criteria
155
     *
156
     * @return QueryBuilder
157
     */
158
    private function getFindByQueryBuilder(array $criteria = []): QueryBuilder
159
    {
160
        $qb = $this->connection->createQueryBuilder();
161
        $qb->select('*')->from($this->getTable());
162
163
        foreach ($criteria as $field => $value) {
164
            $qb->andWhere($qb->expr()->eq($field, ':'.$field));
165
            $qb->setParameter($field, $value);
166
        }
167
168
        return $qb;
169
    }
170
171
    /**
172
     * @param ModelInterface $model
173
     */
174
    public function persist(ModelInterface $model)
175
    {
176
        $this->logger->info(
177
            'model: persist model {model} with id {id}',
178
            ['model' => get_class($model), 'id' => $model->getId()]
179
        );
180
181
        $row = $model->toRow();
182
        foreach ($row as $key => $value) {
183
            if (!is_scalar($value) && !is_array($value)) {
184
                unset($row[$key]);
185
            }
186
        }
187
188
        if (null === $this->find($model->getId())) {
189
            $this->connection->insert($this->getTable(), $row);
190
        } else {
191
            $this->connection->update($this->getTable(), $row, ['id' => $model->getId()]);
192
        }
193
194
        $this->cache->set($model->getId(), $row);
195
    }
196
197
    /**
198
     * @param ModelInterface $model
199
     */
200
    public function remove(ModelInterface $model)
201
    {
202
        $this->logger->info(
203
            'model: remove model {model} with id {id}',
204
            ['model' => get_class($model), 'id' => $model->getId()]
205
        );
206
207
        $this->connection->delete($this->getTable(), ['id' => $model->getId()]);
208
209
        $this->cache->remove($model->getId());
210
    }
211
212
    /**
213
     * @param array $row
214
     * @return ModelInterface
215
     */
216
    protected function fromRow(array $row): ModelInterface
217
    {
218
        /** @var ModelInterface $modelClass */
219
        $modelClass = $this->getModelClass();
220
221
        return $modelClass::fromRow($row);
222
    }
223
224
    /**
225
     * @return string
226
     */
227
    abstract protected function getTable(): string;
228
}
229