Passed
Pull Request — master (#93)
by Damien
03:13
created

UpdateHelper::updateAuditSchema()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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