Passed
Pull Request — master (#93)
by Damien
02:57
created

UpdateHelper::createAuditTable()   B

Complexity

Conditions 7
Paths 4

Size

Total Lines 37
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 7
eloc 21
c 4
b 0
f 0
nc 4
nop 2
dl 0
loc 37
rs 8.6506
1
<?php
2
3
namespace DH\DoctrineAuditBundle\Helper;
4
5
use DH\DoctrineAuditBundle\AuditConfiguration;
6
use DH\DoctrineAuditBundle\Exception\UpdateException;
7
use DH\DoctrineAuditBundle\Manager\AuditManager;
8
use DH\DoctrineAuditBundle\Reader\AuditReader;
9
use Doctrine\DBAL\Schema\Column;
10
use Doctrine\DBAL\Schema\Schema;
11
use Doctrine\DBAL\Schema\Table;
12
13
class UpdateHelper
14
{
15
    /**
16
     * @var AuditManager
17
     */
18
    private $manager;
19
20
    /**
21
     * @var AuditReader
22
     */
23
    private $reader;
24
25
    /**
26
     * @param AuditManager $manager
27
     * @param AuditReader  $reader
28
     */
29
    public function __construct(AuditManager $manager, AuditReader $reader)
30
    {
31
        $this->manager = $manager;
32
        $this->reader = $reader;
33
    }
34
35
    /**
36
     * @return AuditConfiguration
37
     */
38
    public function getConfiguration(): AuditConfiguration
39
    {
40
        return $this->manager->getConfiguration();
41
    }
42
43
    public function updateAuditSchema(): void
44
    {
45
        $auditEntityManager = $this->manager->getConfiguration()->getEntityManager();
46
47
        $sqls = $this->getUpdateAuditSchemaSql();
48
49
        foreach ($sqls as $sql) {
50
            try {
51
                $statement = $auditEntityManager->getConnection()->prepare($sql);
52
                $statement->execute();
53
            } catch (\Exception $e) {
54
                // something bad happened here :/
55
            }
56
        }
57
    }
58
59
    public function getUpdateAuditSchemaSql(): array
60
    {
61
        $readerEntityManager = $this->reader->getEntityManager();
62
        $readerSchemaManager = $readerEntityManager->getConnection()->getSchemaManager();
63
64
        $auditEntityManager = $this->manager->getConfiguration()->getEntityManager();
65
        $auditSchemaManager = $auditEntityManager->getConnection()->getSchemaManager();
66
67
        $auditSchema = $auditSchemaManager->createSchema();
68
        $fromSchema = clone $auditSchema;
69
        $readerSchema = $readerSchemaManager->createSchema();
70
        $tables = $readerSchema->getTables();
71
72
        $entities = $this->reader->getEntities();
73
        foreach ($tables as $table) {
74
            if (\in_array($table->getName(), array_values($entities), true)) {
75
                $auditTablename = preg_replace(
76
                    sprintf('#^([^\.]+\.)?(%s)$#', preg_quote($table->getName(), '#')),
77
                    sprintf(
78
                        '$1%s$2%s',
79
                        preg_quote($this->manager->getConfiguration()->getTablePrefix(), '#'),
80
                        preg_quote($this->manager->getConfiguration()->getTableSuffix(), '#')
81
                    ),
82
                    $table->getName()
83
                );
84
85
                if ($auditSchema->hasTable($auditTablename)) {
86
                    $this->updateAuditTable($auditSchema->getTable($auditTablename), $auditSchema);
87
                } else {
88
                    $this->createAuditTable($table, $auditSchema);
89
                }
90
            }
91
        }
92
93
        return $fromSchema->getMigrateToSql($auditSchema, $auditSchemaManager->getDatabasePlatform());
94
    }
95
96
    /**
97
     * Creates an audit table.
98
     *
99
     * @param Table       $table
100
     * @param null|Schema $schema
101
     *
102
     * @throws \Doctrine\DBAL\DBALException
103
     *
104
     * @return Schema
105
     */
106
    public function createAuditTable(Table $table, ?Schema $schema = null): Schema
107
    {
108
        $entityManager = $this->getConfiguration()->getEntityManager();
109
        $schemaManager = $entityManager->getConnection()->getSchemaManager();
110
        if (null === $schema) {
111
            $schema = $schemaManager->createSchema();
112
        }
113
114
        $auditTablename = preg_replace(
115
            sprintf('#^([^\.]+\.)?(%s)$#', preg_quote($table->getName(), '#')),
116
            sprintf(
117
                '$1%s$2%s',
118
                preg_quote($this->getConfiguration()->getTablePrefix(), '#'),
119
                preg_quote($this->getConfiguration()->getTableSuffix(), '#')
120
            ),
121
            $table->getName()
122
        );
123
124
        if (null !== $auditTablename && !$schema->hasTable($auditTablename)) {
125
            $auditTable = $schema->createTable($auditTablename);
126
127
            // Add columns to audit table
128
            foreach ($this->manager->getHelper()->getAuditTableColumns() as $columnName => $struct) {
129
                $auditTable->addColumn($columnName, $struct['type'], $struct['options']);
130
            }
131
132
            // Add indices to audit table
133
            foreach ($this->manager->getHelper()->getAuditTableIndices($auditTablename) as $columnName => $struct) {
134
                if ('primary' === $struct['type']) {
135
                    $auditTable->setPrimaryKey([$columnName]);
136
                } else {
137
                    $auditTable->addIndex([$columnName], $struct['name']);
138
                }
139
            }
140
        }
141
142
        return $schema;
143
    }
144
145
    /**
146
     * Ensures an audit table's structure is valid.
147
     *
148
     * @param Table       $table
149
     * @param null|Schema $schema
150
     *
151
     * @throws UpdateException
152
     * @throws \Doctrine\DBAL\Schema\SchemaException
153
     *
154
     * @return Schema
155
     */
156
    public function updateAuditTable(Table $table, ?Schema $schema = null): Schema
157
    {
158
        $entityManager = $this->getConfiguration()->getEntityManager();
159
        $schemaManager = $entityManager->getConnection()->getSchemaManager();
160
        if (null === $schema) {
161
            $schema = $schemaManager->createSchema();
162
        }
163
164
        $table = $schema->getTable($table->getName());
165
166
        $columns = $schemaManager->listTableColumns($table->getName());
167
        $expectedColumns = $this->manager->getHelper()->getAuditTableColumns();
168
        $expectedIndices = $this->manager->getHelper()->getAuditTableIndices($table->getName());
169
170
        // process columns
171
        $this->processColumns($table, $columns, $expectedColumns);
172
173
        // process indices
174
        $this->processIndices($table, $expectedIndices);
175
176
        return $schema;
177
    }
178
179
    /**
180
     * @param Table $table
181
     * @param array $columns
182
     * @param array $expectedColumns
183
     */
184
    private function processColumns(Table $table, array $columns, array $expectedColumns): void
185
    {
186
        $processed = [];
187
188
        foreach ($columns as $column) {
189
            if (\array_key_exists($column->getName(), $expectedColumns)) {
190
                // column is part of expected columns
191
                $table->dropColumn($column->getName());
192
                $table->addColumn($column->getName(), $expectedColumns[$column->getName()]['type'], $expectedColumns[$column->getName()]['options']);
193
            } else {
194
                // column is not part of expected columns so it has to be removed
195
                $table->dropColumn($column->getName());
196
            }
197
198
            $processed[] = $column->getName();
199
        }
200
201
        foreach ($expectedColumns as $columnName => $options) {
202
            if (!\in_array($columnName, $processed, true)) {
203
                // expected column in not part of concrete ones so it's a new column, we need to add it
204
                $table->addColumn($columnName, $options['type'], $options['options']);
205
            }
206
        }
207
    }
208
209
    /**
210
     * @param Table $table
211
     * @param array $expectedIndices
212
     *
213
     * @throws \Doctrine\DBAL\Schema\SchemaException
214
     */
215
    private function processIndices(Table $table, array $expectedIndices): void
216
    {
217
        foreach ($expectedIndices as $columnName => $options) {
218
            if ('primary' === $options['type']) {
219
                $table->dropPrimaryKey();
220
                $table->setPrimaryKey([$columnName]);
221
            } else {
222
                if ($table->hasIndex($options['name'])) {
223
                    $table->dropIndex($options['name']);
224
                }
225
                $table->addIndex([$columnName], $options['name']);
226
            }
227
        }
228
    }
229
}
230