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

AuditReader::getAuditsQueryBuilder()   B

Complexity

Conditions 11
Paths 10

Size

Total Lines 48
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 30
nc 10
nop 4
dl 0
loc 48
rs 7.3166
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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->isAudited($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
        return $queryBuilder
0 ignored issues
show
Bug Best Practice introduced by
The expression return $queryBuilder->re...ecute()->fetchColumn(0) could return the type false which is incompatible with the type-hinted return integer. Consider adding an additional type-check to rule them out.
Loading history...
171
            ->resetQueryPart('select')
172
            ->select('COUNT(id)')
173
            ->execute()
174
            ->fetchColumn(0)
175
        ;
176
    }
177
178
    /**
179
     * Returns an array of audited entries/operations.
180
     *
181
     * @param object|string $entity
182
     * @param int|string    $id
183
     * @param int           $page
184
     * @param int           $pageSize
185
     *
186
     * @return QueryBuilder
187
     */
188
    private function getAuditsQueryBuilder($entity, $id = null, ?int $page = null, ?int $pageSize = null): QueryBuilder
189
    {
190
        if (null !== $page && $page < 1) {
191
            throw new \InvalidArgumentException('$page must be greater or equal than 1.');
192
        }
193
194
        if (null !== $pageSize && $pageSize < 1) {
195
            throw new \InvalidArgumentException('$pageSize must be greater or equal than 1.');
196
        }
197
198
        $connection = $this->entityManager->getConnection();
199
        $schema = $this->entityManager->getClassMetadata(\is_string($entity) ? $entity : \get_class($entity))->getSchemaName();
200
201
        $auditTable = implode('', [
202
            null === $schema ? '' : $schema.'.',
203
            $this->configuration->getTablePrefix(),
204
            $this->getEntityTableName(\is_string($entity) ? $entity : \get_class($entity)),
205
            $this->configuration->getTableSuffix(),
206
        ]);
207
208
        $queryBuilder = $connection->createQueryBuilder();
209
        $queryBuilder
210
            ->select('*')
211
            ->from($auditTable, 'at')
212
            ->orderBy('created_at', 'DESC')
213
            ->addOrderBy('id', 'DESC')
214
        ;
215
216
        if (null !== $pageSize) {
217
            $queryBuilder
218
                ->setFirstResult(($page - 1) * $pageSize)
219
                ->setMaxResults($pageSize)
220
            ;
221
        }
222
223
        if (null !== $id) {
224
            $queryBuilder
225
                ->andWhere('object_id = :object_id')
226
                ->setParameter('object_id', $id);
227
        }
228
229
        if (null !== $this->filter) {
230
            $queryBuilder
231
                ->andWhere('type = :filter')
232
                ->setParameter('filter', $this->filter);
233
        }
234
235
        return $queryBuilder;
236
    }
237
238
    /**
239
     * @param object|string $entity
240
     * @param int|string    $id
241
     *
242
     * @return mixed
243
     */
244
    public function getAudit($entity, $id)
245
    {
246
        $connection = $this->entityManager->getConnection();
247
        $schema = $this->entityManager->getClassMetadata(\is_string($entity) ? $entity : \get_class($entity))->getSchemaName();
248
249
        $auditTable = implode('', [
250
            null === $schema ? '' : $schema.'.',
251
            $this->configuration->getTablePrefix(),
252
            $this->getEntityTableName(\is_string($entity) ? $entity : \get_class($entity)),
253
            $this->configuration->getTableSuffix(),
254
        ]);
255
256
        /**
257
         * @var \Doctrine\DBAL\Query\QueryBuilder
258
         */
259
        $queryBuilder = $connection->createQueryBuilder();
260
        $queryBuilder
261
            ->select('*')
262
            ->from($auditTable)
263
            ->where('id = :id')
264
            ->setParameter('id', $id);
265
266
        if (null !== $this->filter) {
267
            $queryBuilder
268
                ->andWhere('type = :filter')
269
                ->setParameter('filter', $this->filter);
270
        }
271
272
        /** @var Statement $statement */
273
        $statement = $queryBuilder->execute();
274
        $statement->setFetchMode(\PDO::FETCH_CLASS, AuditEntry::class);
275
276
        return $statement->fetchAll();
277
    }
278
279
    /**
280
     * Returns the table name of $entity.
281
     *
282
     * @param object|string $entity
283
     *
284
     * @return string
285
     */
286
    public function getEntityTableName($entity): string
287
    {
288
        return $this
289
            ->entityManager
290
            ->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

290
            ->getClassMetadata(/** @scrutinizer ignore-type */ $entity)
Loading history...
291
            ->getTableName();
292
    }
293
}
294