Completed
Push — master ( c703ae...448920 )
by Dominik
01:48
created

AbstractDoctrineRepository::fromPersistence()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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