Passed
Pull Request — master (#77)
by
unknown
03:10
created

AuditReader::getEntityTableName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

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