Completed
Push — master ( bed36a...6638ea )
by Dominik
01:45
created

AbstractDoctrineRepository::persist()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.4285
c 0
b 0
f 0
cc 2
eloc 10
nc 2
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->fromRow($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->fromRow($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->fromRow($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->fromRow($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->toRow();
183
184
        if (null === $this->find($model->getId())) {
185
            $this->connection->insert($this->getTable(), $row);
186
        } else {
187
            $this->connection->update($this->getTable(), $row, ['id' => $model->getId()]);
188
        }
189
190
        $this->cache->set($model->getId(), $row);
191
    }
192
193
    /**
194
     * @param ModelInterface $model
195
     */
196
    public function remove(ModelInterface $model)
197
    {
198
        $this->logger->info(
199
            'model: remove model {model} with id {id}',
200
            ['model' => get_class($model), 'id' => $model->getId()]
201
        );
202
203
        $this->connection->delete($this->getTable(), ['id' => $model->getId()]);
204
205
        $this->cache->remove($model->getId());
206
    }
207
208
    /**
209
     * @param array $row
210
     *
211
     * @return ModelInterface
212
     */
213
    protected function fromRow(array $row): ModelInterface
214
    {
215
        /** @var ModelInterface $modelClass */
216
        $modelClass = $this->getModelClass();
217
218
        return $modelClass::fromRow($row);
219
    }
220
221
    /**
222
     * @return string
223
     */
224
    abstract protected function getTable(): string;
225
}
226