Passed
Pull Request — master (#68)
by Maxime
03:01
created

AuditReader::getClassMetadata()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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