Passed
Pull Request — master (#50)
by Damien
02:13
created

AuditReader::getAudit()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 34
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 20
nc 4
nop 2
dl 0
loc 34
rs 9.6
c 0
b 0
f 0
1
<?php
2
3
namespace DH\DoctrineAuditBundle\Reader;
4
5
use DH\DoctrineAuditBundle\AuditConfiguration;
6
use Doctrine\DBAL\Statement;
7
use Doctrine\ORM\EntityManagerInterface;
8
9
class AuditReader
10
{
11
    const UPDATE = 'update';
12
    const ASSOCIATE = 'associate';
13
    const DISSOCIATE = 'dissociate';
14
    const INSERT = 'insert';
15
    const REMOVE = 'remove';
16
17
    /**
18
     * @var AuditConfiguration
19
     */
20
    private $configuration;
21
22
    /**
23
     * @var EntityManagerInterface
24
     */
25
    private $entityManager;
26
27
    /**
28
     * @var ?string
29
     */
30
    private $filter;
31
32
    /**
33
     * AuditReader constructor.
34
     *
35
     * @param AuditConfiguration     $configuration
36
     * @param EntityManagerInterface $entityManager
37
     */
38
    public function __construct(AuditConfiguration $configuration, EntityManagerInterface $entityManager)
39
    {
40
        $this->configuration = $configuration;
41
        $this->entityManager = $entityManager;
42
    }
43
44
    /**
45
     * @return AuditConfiguration
46
     */
47
    public function getConfiguration(): AuditConfiguration
48
    {
49
        return $this->configuration;
50
    }
51
52
    /**
53
     * Set the filter for AuditEntry retrieving.
54
     *
55
     * @param string $filter
56
     *
57
     * @return AuditReader
58
     */
59
    public function filterBy(string $filter): self
60
    {
61
        if (!\in_array($filter, [self::UPDATE, self::ASSOCIATE, self::DISSOCIATE, self::INSERT, self::REMOVE], true)) {
62
            $this->filter = null;
63
        } else {
64
            $this->filter = $filter;
65
        }
66
67
        return $this;
68
    }
69
70
    /**
71
     * Returns current filter.
72
     *
73
     * @return null|string
74
     */
75
    public function getFilter(): ?string
76
    {
77
        return $this->filter;
78
    }
79
80
    /**
81
     * Returns an array of audit table names indexed by entity FQN.
82
     *
83
     * @throws \Doctrine\ORM\ORMException
84
     *
85
     * @return array
86
     */
87
    public function getEntities(): array
88
    {
89
        $metadataDriver = $this->entityManager->getConfiguration()->getMetadataDriverImpl();
90
        $entities = [];
91
        if (null !== $metadataDriver) {
92
            $entities = $metadataDriver->getAllClassNames();
93
        }
94
        $audited = [];
95
        foreach ($entities as $entity) {
96
            if ($this->configuration->isAudited($entity)) {
97
                $audited[$entity] = $this->getEntityTableName($entity);
98
            }
99
        }
100
        ksort($audited);
101
102
        return $audited;
103
    }
104
105
    /**
106
     * Returns an array of audited entries/operations.
107
     *
108
     * @param object|string $entity
109
     * @param null|int      $id
110
     * @param int           $page
111
     * @param int           $pageSize
112
     *
113
     * @return array
114
     */
115
    public function getAudits($entity, int $id = null, int $page = 1, int $pageSize = 50): array
116
    {
117
        if ($page < 1) {
118
            throw new \InvalidArgumentException('$page must be greater or equal than 1.');
119
        }
120
        if ($pageSize < 1) {
121
            throw new \InvalidArgumentException('$pageSize must be greater or equal than 1.');
122
        }
123
124
        $connection = $this->entityManager->getConnection();
125
126
        $schema = isset($this->entityManager->getClassMetadata($entity)->table['schema']) ? $this->entityManager->getClassMetadata($entity)->table['schema'].'.' : '';
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

126
        $schema = isset($this->entityManager->getClassMetadata(/** @scrutinizer ignore-type */ $entity)->table['schema']) ? $this->entityManager->getClassMetadata($entity)->table['schema'].'.' : '';
Loading history...
Bug introduced by
Accessing table on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
127
128
        $auditTable = implode('', [
129
            $schema,
130
            $this->configuration->getTablePrefix(),
131
            $this->getEntityTableName(\is_string($entity) ? $entity : \get_class($entity)),
132
            $this->configuration->getTableSuffix(),
133
        ]);
134
135
        $queryBuilder = $connection->createQueryBuilder();
136
        $queryBuilder
137
            ->select('*')
138
            ->from($auditTable)
139
            ->orderBy('created_at', 'DESC')
140
            ->addOrderBy('id', 'DESC')
141
            ->setFirstResult(($page - 1) * $pageSize)
142
            ->setMaxResults($pageSize);
143
144
        if (null !== $id) {
145
            $queryBuilder
146
                ->where('object_id = :object_id')
147
                ->setParameter('object_id', $id);
148
        }
149
150
        if (null !== $this->filter) {
151
            $queryBuilder
152
                ->andWhere('type = :filter')
153
                ->setParameter('filter', $this->filter);
154
        }
155
156
        /** @var Statement $statement */
157
        $statement = $queryBuilder->execute();
158
        $statement->setFetchMode(\PDO::FETCH_CLASS, AuditEntry::class);
159
160
        return $statement->fetchAll();
161
    }
162
163
    /**
164
     * @param object|string $entity
165
     * @param int           $id
166
     *
167
     * @return mixed
168
     */
169
    public function getAudit($entity, int $id)
170
    {
171
        $connection = $this->entityManager->getConnection();
172
173
        $schema = isset($this->entityManager->getClassMetadata($entity)->table['schema']) ? $this->entityManager->getClassMetadata($entity)->table['schema'].'.' : '';
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

173
        $schema = isset($this->entityManager->getClassMetadata(/** @scrutinizer ignore-type */ $entity)->table['schema']) ? $this->entityManager->getClassMetadata($entity)->table['schema'].'.' : '';
Loading history...
Bug introduced by
Accessing table on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
174
175
        $auditTable = implode('', [
176
            $schema,
177
            $this->configuration->getTablePrefix(),
178
            $this->getEntityTableName(\is_string($entity) ? $entity : \get_class($entity)),
179
            $this->configuration->getTableSuffix(),
180
        ]);
181
182
        /**
183
         * @var \Doctrine\DBAL\Query\QueryBuilder
184
         */
185
        $queryBuilder = $connection->createQueryBuilder();
186
        $queryBuilder
187
            ->select('*')
188
            ->from($auditTable)
189
            ->where('id = :id')
190
            ->setParameter('id', $id);
191
192
        if (null !== $this->filter) {
193
            $queryBuilder
194
                ->andWhere('type = :filter')
195
                ->setParameter('filter', $this->filter);
196
        }
197
198
        /** @var Statement $statement */
199
        $statement = $queryBuilder->execute();
200
        $statement->setFetchMode(\PDO::FETCH_CLASS, AuditEntry::class);
201
202
        return $statement->fetchAll();
203
    }
204
205
    /**
206
     * Returns the table name of $entity.
207
     *
208
     * @param object|string $entity
209
     *
210
     * @return string
211
     */
212
    public function getEntityTableName($entity): string
213
    {
214
        return $this
215
            ->entityManager
216
            ->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

216
            ->getClassMetadata(/** @scrutinizer ignore-type */ $entity)
Loading history...
217
            ->table['name'];
0 ignored issues
show
Bug introduced by
Accessing table on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
218
    }
219
}
220