Passed
Pull Request — master (#187)
by
unknown
12:29
created

Configuration::setExtraFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 2
dl 0
loc 5
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace DH\Auditor\Provider\Doctrine;
6
7
use DH\Auditor\Provider\ConfigurationInterface;
8
use DH\Auditor\Provider\Doctrine\Persistence\Helper\DoctrineHelper;
9
use DH\Auditor\Provider\Doctrine\Persistence\Helper\SchemaHelper;
10
use DH\Auditor\Provider\Doctrine\Persistence\Schema\SchemaManager;
11
use DH\Auditor\Provider\Doctrine\Service\AuditingService;
12
use Symfony\Component\OptionsResolver\OptionsResolver;
13
14
/**
15
 * @see \DH\Auditor\Tests\Provider\Doctrine\ConfigurationTest
16
 */
17
final class Configuration implements ConfigurationInterface
18
{
19
    private ?DoctrineProvider $provider = null;
20
21
    private string $tablePrefix;
22
23
    private string $tableSuffix;
24
25
    private array $ignoredColumns;
26
27
    private ?array $entities = null;
28
29
    private array $extraFields = [];
30
31
    private array $extraIndices = [];
32
33
    private array $storageServices = [];
34
35
    private array $auditingServices = [];
36
37
    private bool $isViewerEnabled;
38
39
    private bool $initialized = false;
40
41
    /**
42
     * @var null|callable
43
     */
44
    private $storageMapper;
45
46
    private array $annotationLoaded = [];
47
48
    public function __construct(array $options)
49
    {
50
        $resolver = new OptionsResolver();
51
        $this->configureOptions($resolver);
52
        $config = $resolver->resolve($options);
53
54
        $this->tablePrefix = $config['table_prefix'];
55
        $this->tableSuffix = $config['table_suffix'];
56
        $this->ignoredColumns = $config['ignored_columns'];
57
58
        if (isset($config['entities']) && !empty($config['entities'])) {
59
            // use entity names as array keys for easier lookup
60
            foreach ($config['entities'] as $auditedEntity => $entityOptions) {
61
                $this->entities[$auditedEntity] = $entityOptions;
62
            }
63
        }
64
65
        if (isset($config['extra_fields']) && !empty($config['extra_fields'])) {
66
            // use field names as array keys for easier lookup
67
            foreach ($config['extra_fields'] as $fieldName => $fieldOptions) {
68
                $this->extraFields[$fieldName] = $fieldOptions;
69
            }
70
        }
71
72
        if (isset($config['extra_indices']) && !empty($config['extra_indices'])) {
73
            // use index names as array keys for easier lookup
74
            foreach ($config['extra_indices'] as $indexName => $indexOptions) {
75
                $this->extraIndices[$indexName] = $indexOptions;
76
            }
77
        }
78
79
        $this->storageServices = $config['storage_services'];
80
        $this->auditingServices = $config['auditing_services'];
81
        $this->isViewerEnabled = $config['viewer'];
82
        $this->storageMapper = $config['storage_mapper'];
83
    }
84
85
    /**
86
     * Set the value of entities.
87
     *
88
     * This method completely overrides entities configuration
89
     * including annotation configuration
90
     *
91
     * @param array<int|string, mixed> $entities
92
     */
93
    public function setEntities(array $entities): self
94
    {
95
        $this->entities = $entities;
96
        $this->initialized = false;
97
98
        return $this;
99
    }
100
101
    /**
102
     * enable audit Controller and its routing.
103
     *
104
     * @return $this
105
     */
106
    public function enableViewer(): self
107
    {
108
        $this->isViewerEnabled = true;
109
110
        return $this;
111
    }
112
113
    /**
114
     * disable audit Controller and its routing.
115
     *
116
     * @return $this
117
     */
118
    public function disableViewer(): self
119
    {
120
        $this->isViewerEnabled = false;
121
122
        return $this;
123
    }
124
125
    /**
126
     * Get enabled flag.
127
     */
128
    public function isViewerEnabled(): bool
129
    {
130
        return $this->isViewerEnabled;
131
    }
132
133
    /**
134
     * Get the value of tablePrefix.
135
     */
136
    public function getTablePrefix(): string
137
    {
138
        return $this->tablePrefix;
139
    }
140
141
    /**
142
     * Get the value of tableSuffix.
143
     */
144
    public function getTableSuffix(): string
145
    {
146
        return $this->tableSuffix;
147
    }
148
149
    /**
150
     * Get the value of excludedColumns.
151
     *
152
     * @return array<string>
153
     */
154
    public function getIgnoredColumns(): array
155
    {
156
        return $this->ignoredColumns;
157
    }
158
159
    /**
160
     * Get the value of entities.
161
     */
162
    public function getEntities(): array
163
    {
164
        if ($this->initialized && null !== $this->entities) {
165
            return $this->entities;
166
        }
167
168
        if ($this->provider instanceof \DH\Auditor\Provider\Doctrine\DoctrineProvider) {
169
            $schemaManager = new SchemaManager($this->provider);
170
171
            /** @var array<AuditingService> $auditingServices */
172
            $auditingServices = $this->provider->getAuditingServices();
173
            foreach ($auditingServices as $auditingService) {
174
                $entityManager = $auditingService->getEntityManager();
175
                $platform = $entityManager->getConnection()->getDatabasePlatform();
176
177
                // do not load annotations if they're already loaded
178
                if (!isset($this->annotationLoaded[$auditingService->getName()]) || !$this->annotationLoaded[$auditingService->getName()]) {
179
                    $this->provider->loadAnnotations($entityManager, $this->entities ?? []);
180
                    $this->annotationLoaded[$auditingService->getName()] = true;
181
                }
182
183
                \assert(null !== $this->entities);
184
                foreach (array_keys($this->entities) as $entity) {
185
                    $meta = $entityManager->getClassMetadata(DoctrineHelper::getRealClassName($entity));
186
                    $entityTableName = $meta->getTableName();
187
                    $namespaceName = $meta->getSchemaName() ?? '';
188
189
                    $computedTableName = $schemaManager->resolveTableName($entityTableName, $namespaceName, $platform);
190
                    $this->entities[$entity]['table_schema'] = $namespaceName;
191
                    $this->entities[$entity]['table_name'] = $entityTableName;
192
                    //                    $this->entities[$entity]['computed_table_name'] = $entityTableName;
193
                    $this->entities[$entity]['computed_table_name'] = $computedTableName;
194
                    $this->entities[$entity]['audit_table_schema'] = $namespaceName;
195
                    $this->entities[$entity]['audit_table_name'] = $schemaManager->computeAuditTablename($entityTableName, $this);
196
                    //                    $this->entities[$entity]['computed_audit_table_name'] = $schemaManager->computeAuditTablename($this->entities[$entity], $this, $platform);
197
                    $this->entities[$entity]['computed_audit_table_name'] = $schemaManager->computeAuditTablename(
198
                        $computedTableName,
199
                        $this
200
                    );
201
                }
202
            }
203
204
            $this->initialized = true;
205
        }
206
207
        return $this->entities ?? [];
208
    }
209
210
    public function getExtraFields(): array
211
    {
212
        return $this->extraFields;
213
    }
214
215
    public function getAllFields(): array
216
    {
217
        return array_merge(
218
            SchemaHelper::getAuditTableColumns(),
219
            $this->extraFields
220
        );
221
    }
222
223
    /**
224
     * @param array<string, mixed> $extraFields
225
     */
226
    public function setExtraFields(array $extraFields): self
227
    {
228
        $this->extraFields = $extraFields;
229
230
        return $this;
231
    }
232
233
    public function getExtraIndices(): array
234
    {
235
        return $this->extraIndices;
236
    }
237
238
    public function prepareExtraIndices(string $tablename): array
239
    {
240
        $indices = [];
241
        foreach ($this->extraIndices as $extraIndexField => $extraIndexOptions) {
242
            $indices[$extraIndexField] = [
243
                'type' => $extraIndexOptions['type'] ?? 'index',
244
                'name' => sprintf('%s_%s_idx', $extraIndexOptions['name_prefix'] ?? $extraIndexField, md5($tablename)),
245
            ];
246
        }
247
248
        return $indices;
249
    }
250
251
    public function getAllIndices(string $tablename): array
252
    {
253
        return array_merge(
254
            SchemaHelper::getAuditTableIndices($tablename),
255
            $this->prepareExtraIndices($tablename)
256
        );
257
    }
258
259
    /**
260
     * @param array<string, mixed> $extraIndices
261
     */
262
    public function setExtraIndices(array $extraIndices): self
263
    {
264
        $this->extraIndices = $extraIndices;
265
266
        return $this;
267
    }
268
269
    /**
270
     * Enables auditing for a specific entity.
271
     *
272
     * @param string $entity Entity class name
273
     *
274
     * @return $this
275
     */
276
    public function enableAuditFor(string $entity): self
277
    {
278
        if (isset($this->getEntities()[$entity])) {
279
            $this->entities[$entity]['enabled'] = true;
280
        }
281
282
        return $this;
283
    }
284
285
    /**
286
     * Disables auditing for a specific entity.
287
     *
288
     * @param string $entity Entity class name
289
     *
290
     * @return $this
291
     */
292
    public function disableAuditFor(string $entity): self
293
    {
294
        if (isset($this->getEntities()[$entity])) {
295
            $this->entities[$entity]['enabled'] = false;
296
        }
297
298
        return $this;
299
    }
300
301
    public function setStorageMapper(callable $mapper): self
302
    {
303
        $this->storageMapper = $mapper;
304
305
        return $this;
306
    }
307
308
    /**
309
     * @return null|callable|string
310
     */
311
    public function getStorageMapper(): mixed
312
    {
313
        return $this->storageMapper;
314
    }
315
316
    public function getProvider(): ?DoctrineProvider
317
    {
318
        return $this->provider;
319
    }
320
321
    public function setProvider(DoctrineProvider $provider): void
322
    {
323
        $this->provider = $provider;
324
        $this->initialized = false;
325
    }
326
327
    private function configureOptions(OptionsResolver $resolver): void
328
    {
329
        // https://symfony.com/doc/current/components/options_resolver.html
330
        $resolver
331
            ->setDefaults([
332
                'table_prefix' => '',
333
                'table_suffix' => '_audit',
334
                'ignored_columns' => [],
335
                'entities' => [],
336
                'extra_fields' => [],
337
                'extra_indices' => [],
338
                'storage_services' => [],
339
                'auditing_services' => [],
340
                'viewer' => true,
341
                'storage_mapper' => null,
342
            ])
343
            ->setAllowedTypes('table_prefix', 'string')
344
            ->setAllowedTypes('table_suffix', 'string')
345
            ->setAllowedTypes('ignored_columns', 'array')
346
            ->setAllowedTypes('entities', 'array')
347
            ->setAllowedTypes('extra_fields', 'array')
348
            ->setAllowedTypes('extra_indices', 'array')
349
            ->setAllowedTypes('storage_services', 'array')
350
            ->setAllowedTypes('auditing_services', 'array')
351
            ->setAllowedTypes('viewer', 'bool')
352
            ->setAllowedTypes('storage_mapper', ['null', 'string', 'callable'])
353
        ;
354
    }
355
}
356