Passed
Pull Request — master (#60)
by Damien
02:16
created

AuditReader::getAuditsCount()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

293
            ->getClassMetadata(/** @scrutinizer ignore-type */ $entity)
Loading history...
294
            ->getTableName();
295
    }
296
}
297