Passed
Pull Request — master (#77)
by
unknown
02:45
created

AuditReader::selectStorage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
3
namespace DH\DoctrineAuditBundle\Reader;
4
5
use DH\DoctrineAuditBundle\AuditConfiguration;
6
use Doctrine\DBAL\Query\QueryBuilder;
7
use Doctrine\DBAL\Statement;
8
use Doctrine\ORM\EntityManagerInterface;
9
use Doctrine\ORM\Mapping\ClassMetadata;
10
use Pagerfanta\Adapter\DoctrineDbalSingleTableAdapter;
11
use Pagerfanta\Pagerfanta;
12
13
class AuditReader
14
{
15
    const UPDATE = 'update';
16
    const ASSOCIATE = 'associate';
17
    const DISSOCIATE = 'dissociate';
18
    const INSERT = 'insert';
19
    const REMOVE = 'remove';
20
21
    const PAGE_SIZE = 50;
22
23
    /**
24
     * @var AuditConfiguration
25
     */
26
    private $configuration;
27
28
    /**
29
     * @var EntityManagerInterface
30
     */
31
    private $entityManager;
32
33
    /**
34
     * @var EntityManagerInterface|null
35
     */
36
    private $customStorageEntityManager;
37
38
    /**
39
     * @var ?string
40
     */
41
    private $filter;
42
43
    /**
44
     * AuditReader constructor.
45
     *
46
     * @param AuditConfiguration     $configuration
47
     * @param EntityManagerInterface $entityManager
48
     */
49
    public function __construct(
50
        AuditConfiguration $configuration,
51
        EntityManagerInterface $entityManager,
52
        ?EntityManagerInterface $customStorageEntityManager = null
53
    ) {
54
        $this->configuration = $configuration;
55
        $this->entityManager = $entityManager;
56
        $this->customStorageEntityManager = $customStorageEntityManager;
57
    }
58
59
    /**
60
     * @return AuditConfiguration
61
     */
62
    public function getConfiguration(): AuditConfiguration
63
    {
64
        return $this->configuration;
65
    }
66
67
    /**
68
     * Set the filter for AuditEntry retrieving.
69
     *
70
     * @param string $filter
71
     *
72
     * @return AuditReader
73
     */
74
    public function filterBy(string $filter): self
75
    {
76
        if (!\in_array($filter, [self::UPDATE, self::ASSOCIATE, self::DISSOCIATE, self::INSERT, self::REMOVE], true)) {
77
            $this->filter = null;
78
        } else {
79
            $this->filter = $filter;
80
        }
81
82
        return $this;
83
    }
84
85
    /**
86
     * Returns current filter.
87
     *
88
     * @return null|string
89
     */
90
    public function getFilter(): ?string
91
    {
92
        return $this->filter;
93
    }
94
95
    /**
96
     * Returns an array of audit table names indexed by entity FQN.
97
     *
98
     * @throws \Doctrine\ORM\ORMException
99
     *
100
     * @return array
101
     */
102
    public function getEntities(): array
103
    {
104
        $metadataDriver = $this->entityManager->getConfiguration()->getMetadataDriverImpl();
105
        $entities = [];
106
        if (null !== $metadataDriver) {
107
            $entities = $metadataDriver->getAllClassNames();
108
        }
109
        $audited = [];
110
        foreach ($entities as $entity) {
111
            if ($this->configuration->isAuditable($entity)) {
112
                $audited[$entity] = $this->getEntityTableName($entity);
113
            }
114
        }
115
        ksort($audited);
116
117
        return $audited;
118
    }
119
120
    /**
121
     * Returns an array of audited entries/operations.
122
     *
123
     * @param object|string $entity
124
     * @param int|string    $id
125
     * @param null|int      $page
126
     * @param null|int      $pageSize
127
     *
128
     * @return array
129
     */
130
    public function getAudits($entity, $id = null, ?int $page = null, ?int $pageSize = null): array
131
    {
132
        $queryBuilder = $this->getAuditsQueryBuilder($entity, $id, $page, $pageSize);
133
134
        /** @var Statement $statement */
135
        $statement = $queryBuilder->execute();
136
        $statement->setFetchMode(\PDO::FETCH_CLASS, AuditEntry::class);
137
138
        return $statement->fetchAll();
139
    }
140
141
    /**
142
     * Returns an array of audited entries/operations.
143
     *
144
     * @param object|string $entity
145
     * @param int|string    $id
146
     * @param null|int      $page
147
     * @param null|int      $pageSize
148
     *
149
     * @return Pagerfanta
150
     */
151
    public function getAuditsPager($entity, $id = null, int $page = 1, int $pageSize = self::PAGE_SIZE): Pagerfanta
152
    {
153
        $queryBuilder = $this->getAuditsQueryBuilder($entity, $id);
154
155
        $adapter = new DoctrineDbalSingleTableAdapter($queryBuilder, 'at.id');
156
157
        $pagerfanta = new Pagerfanta($adapter);
158
        $pagerfanta
159
            ->setMaxPerPage($pageSize)
160
            ->setCurrentPage($page)
161
        ;
162
163
        return $pagerfanta;
164
    }
165
166
    /**
167
     * Returns the amount of audited entries/operations.
168
     *
169
     * @param object|string $entity
170
     * @param int|string    $id
171
     *
172
     * @throws \Doctrine\ORM\NonUniqueResultException
173
     *
174
     * @return int
175
     */
176
    public function getAuditsCount($entity, $id = null): int
177
    {
178
        $queryBuilder = $this->getAuditsQueryBuilder($entity, $id);
179
180
        $result = $queryBuilder
181
            ->resetQueryPart('select')
182
            ->resetQueryPart('orderBy')
183
            ->select('COUNT(id)')
184
            ->execute()
185
            ->fetchColumn(0)
186
        ;
187
188
        return false === $result ? 0 : $result;
189
    }
190
191
    /**
192
     * Returns an array of audited entries/operations.
193
     *
194
     * @param object|string $entity
195
     * @param int|string    $id
196
     * @param int           $page
197
     * @param int           $pageSize
198
     *
199
     * @return QueryBuilder
200
     */
201
    private function getAuditsQueryBuilder($entity, $id = null, ?int $page = null, ?int $pageSize = null): QueryBuilder
202
    {
203
        if (null !== $page && $page < 1) {
204
            throw new \InvalidArgumentException('$page must be greater or equal than 1.');
205
        }
206
207
        if (null !== $pageSize && $pageSize < 1) {
208
            throw new \InvalidArgumentException('$pageSize must be greater or equal than 1.');
209
        }
210
211
        $storage = $this->selectStorage();
212
        $connection = $storage->getConnection();
213
214
        $queryBuilder = $connection->createQueryBuilder();
215
        $queryBuilder
216
            ->select('*')
217
            ->from($this->getEntityAuditTableName($entity), 'at')
218
            ->orderBy('created_at', 'DESC')
219
            ->addOrderBy('id', 'DESC')
220
        ;
221
222
        if (null !== $pageSize) {
223
            $queryBuilder
224
                ->setFirstResult(($page - 1) * $pageSize)
225
                ->setMaxResults($pageSize)
226
            ;
227
        }
228
229
        if (null !== $id) {
230
            $queryBuilder
231
                ->andWhere('object_id = :object_id')
232
                ->setParameter('object_id', $id);
233
        }
234
235
        if (null !== $this->filter) {
236
            $queryBuilder
237
                ->andWhere('type = :filter')
238
                ->setParameter('filter', $this->filter);
239
        }
240
241
        return $queryBuilder;
242
    }
243
244
    /**
245
     * @param object|string $entity
246
     * @param int|string    $id
247
     *
248
     * @return mixed
249
     */
250
    public function getAudit($entity, $id)
251
    {
252
        $connection = $this->entityManager->getConnection();
253
254
        /**
255
         * @var \Doctrine\DBAL\Query\QueryBuilder
256
         */
257
        $queryBuilder = $connection->createQueryBuilder();
258
        $queryBuilder
259
            ->select('*')
260
            ->from($this->getEntityAuditTableName($entity))
261
            ->where('id = :id')
262
            ->setParameter('id', $id);
263
264
        if (null !== $this->filter) {
265
            $queryBuilder
266
                ->andWhere('type = :filter')
267
                ->setParameter('filter', $this->filter);
268
        }
269
270
        /** @var Statement $statement */
271
        $statement = $queryBuilder->execute();
272
        $statement->setFetchMode(\PDO::FETCH_CLASS, AuditEntry::class);
273
274
        return $statement->fetchAll();
275
    }
276
277
    private function getClassMetadata($entity): ClassMetadata
278
    {
279
        return $this
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->entityMana...tClassMetadata($entity) returns the type Doctrine\Common\Persistence\Mapping\ClassMetadata which includes types incompatible with the type-hinted return Doctrine\ORM\Mapping\ClassMetadata.
Loading history...
280
            ->entityManager
281
            ->getClassMetadata($entity);
282
    }
283
284
    /**
285
     * Returns the table name of $entity.
286
     *
287
     * @param object|string $entity
288
     *
289
     * @return string
290
     */
291
    public function getEntityTableName($entity): string
292
    {
293
        return $this->getClassMetadata($entity)
294
            ->getTableName();
295
    }
296
297
    /**
298
     * Returns the audit table name for $entity.
299
     *
300
     * @param $entity
301
     *
302
     * @return string
303
     */
304
    public function getEntityAuditTableName($entity): string
305
    {
306
        $entityName = \is_string($entity) ? $entity : \get_class($entity);
307
        $schema = $this->getClassMetadata($entityName)->getSchemaName() ? $this->getClassMetadata($entityName)->getSchemaName().'.' : '';
308
309
        return sprintf('%s%s%s%s', $schema, $this->configuration->getTablePrefix(), $this->getEntityTableName($entityName), $this->configuration->getTableSuffix());
310
    }
311
312
    /**
313
     * @return EntityManagerInterface
314
     */
315
    private function selectStorage(): EntityManagerInterface
316
    {
317
        return $this->customStorageEntityManager ?? $this->entityManager;
318
    }
319
}
320