AlterAuditTable::compareTableOptions()   B
last analyzed

Complexity

Conditions 6
Paths 6

Size

Total Lines 33
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 18
CRAP Score 6.0052

Importance

Changes 0
Metric Value
cc 6
eloc 21
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 33
ccs 18
cts 19
cp 0.9474
crap 6.0052
rs 8.9617
1
<?php
2
declare(strict_types=1);
3
4
namespace SetBased\Audit\Audit;
5
6
use SetBased\Audit\Metadata\TableColumnsMetadata;
7
use SetBased\Audit\MySql\AlterTableCodeStore;
8
use SetBased\Audit\MySql\AuditDataLayer;
9
use SetBased\Audit\MySql\Metadata\TableMetadata;
10
use SetBased\Config\TypedConfig;
11
use SetBased\Exception\FallenException;
12
13
/**
14
 * Class for generating alter audit table SQL statements for manual evaluation.
15
 */
16
class AlterAuditTable
17
{
18
  //--------------------------------------------------------------------------------------------------------------------
19
  /**
20
   * The metadata of the additional audit columns.
21
   *
22
   * @var TableColumnsMetadata
23
   */
24
  private TableColumnsMetadata $additionalAuditColumns;
25
26
  /**
27
   * Code store for alter table statement.
28
   *
29
   * @var AlterTableCodeStore
30
   */
31
  private AlterTableCodeStore $codeStore;
32
33
  /**
34
   * The strong typed configuration reader and writer.
35
   *
36
   * @var TypedConfig
37
   */
38
  private TypedConfig $config;
39
40
  //--------------------------------------------------------------------------------------------------------------------
41
  /**
42
   * Object constructor.
43
   *
44
   * @param TypedConfig $config The strong typed configuration reader and writer.
45
   */
46 8
  public function __construct(TypedConfig $config)
47
  {
48 8
    $this->config    = $config;
49 8
    $this->codeStore = new AlterTableCodeStore();
50
51 8
    $this->additionalAuditColumns =
52 8
      AuditDataLayer::$dl->resolveCanonicalAdditionalAuditColumns($this->config->getManString('database.audit_schema'),
53 8
                                                                  $this->config->getManArray('audit_columns'));
54 8
  }
55
56
  //--------------------------------------------------------------------------------------------------------------------
57
  /**
58
   * The main method: executes the creates alter table statement actions for tables.
59
   *
60
   * return string
61
   */
62 8
  public function main(): string
63
  {
64 8
    $tables = $this->getTableList();
65 8
    foreach ($tables as $table)
66
    {
67 8
      $this->compareTable($table);
68
    }
69
70 8
    return $this->codeStore->getCode();
71
  }
72
73
  //--------------------------------------------------------------------------------------------------------------------
74
  /**
75
   * Compares a table in the data schema and its counterpart in the audit schema.
76
   *
77
   * @param string $tableName The name of the table.
78
   */
79 8
  private function compareTable(string $tableName): void
80
  {
81 8
    $dataTable  = $this->getTableMetadata($this->config->getManString('database.data_schema'), $tableName);
82 8
    $auditTable = $this->getTableMetadata($this->config->getManString('database.audit_schema'), $tableName);
83
84
    // In the audit schema columns corresponding with the columns from the data table are always nullable.
85 8
    $dataTable->getColumns()
86 8
              ->makeNullable();
87
    $dataTable->getColumns()
88 8
              ->prependTableColumns($this->additionalAuditColumns);
89 8
90 8
    $this->compareTableOptions($dataTable, $auditTable);
91
    $this->compareTableColumns($dataTable, $auditTable);
92
  }
93
94
  //--------------------------------------------------------------------------------------------------------------------
95
  /**
96
   * Compares the columns of the data and audit tables and generates the appropriate alter table statement.
97
   *
98
   * @param TableMetadata $dataTable  The metadata of the data table.
99 8
   * @param TableMetadata $auditTable The metadata of the audit table.
100
   */
101 8
  private function compareTableColumns(TableMetadata $dataTable, TableMetadata $auditTable): void
102
  {
103 8
    $diff = TableColumnsMetadata::differentColumnTypes($auditTable->getColumns(), $dataTable->getColumns());
104
105 5
    if (!empty($diff->getColumns()))
106
    {
107 5
      $maxLength = $diff->getLongestColumnNameLength();
108 5
109 5
      $this->codeStore->append(sprintf('alter table `%s`.`%s`',
110
                                       $this->config->getManString('database.audit_schema'),
111 5
                                       $auditTable->getTableName()));
112 5
113
      $first = true;
114 5
      foreach ($diff->getColumns() as $column)
115 5
      {
116
        $name   = $column->getName();
117 5
        $filler = str_repeat(' ', $maxLength - mb_strlen($name) + 1);
118
119 5
        if (!$first)
120
        {
121
          $this->codeStore->appendToLastLine(',');
122
        }
123
124 5
        $this->codeStore->append(sprintf('change column `%s`%s`%s`%s%s',
125
                                         $name,
126 5
                                         $filler,
127
                                         $name,
128
                                         $filler,
129 5
                                         $column->getColumnAuditDefinition()));
130 5
131
        $first = false;
132 8
      }
133
134
      $this->codeStore->append(';');
135
      $this->codeStore->append('');
136
    }
137
  }
138
139
  //--------------------------------------------------------------------------------------------------------------------
140
  /**
141 8
   * Compares the table options of the data and audit tables and generates the appropriate alter table statement.
142
   *
143 8
   * @param TableMetadata $dataTable  The metadata of the data table.
144
   * @param TableMetadata $auditTable The metadata of the audit table.
145 8
   */
146
  private function compareTableOptions(TableMetadata $dataTable, TableMetadata $auditTable): void
147 4
  {
148 4
    $options = TableMetadata::compareOptions($dataTable, $auditTable);
149
150 4
    if (!empty($options))
151
    {
152 4
      $parts = [];
153 2
      foreach ($options as $option)
154 2
      {
155
        switch ($option)
156 3
        {
157 2
          case 'engine':
158 2
            $parts[] = 'engine '.$dataTable->getProperty('engine');
159
            break;
160 3
161 3
          case 'character_set_name':
162 3
            $parts[] = 'default character set '.$dataTable->getProperty('character_set_name');
163
            break;
164
165
          case 'table_collation':
166
            $parts[] = 'default collate '.$dataTable->getProperty('table_collation');
167
            break;
168
169 4
          default:
170 4
            throw new FallenException('option', $option);
171 4
        }
172 4
      }
173 4
174
      $this->codeStore->append(sprintf('alter table `%s`.`%s` %s;',
175 8
                                       $this->config->getManString('database.audit_schema'),
176
                                       $auditTable->getTableName(),
177
                                       implode(' ', $parts)));
178
      $this->codeStore->append('');
179
    }
180
  }
181
182
  //--------------------------------------------------------------------------------------------------------------------
183 8
  /**
184
   * Returns the names of the tables that must be compared.
185 8
   *
186 8
   * @return string[]
187
   */
188 8
  private function getTableList(): array
189
  {
190 8
    $tables1 = [];
191
    foreach ($this->config->getManArray('tables') as $tableName => $config)
192
    {
193
      if ($config['audit'])
194 8
      {
195 8
        $tables1[] = $tableName;
196 8
      }
197
    }
198 8
199
    $tables  = AuditDataLayer::$dl->getTablesNames($this->config->getManString('database.data_schema'));
200
    $tables2 = [];
201 8
    foreach ($tables as $table)
202 8
    {
203 8
      $tables2[] = $table['table_name'];
204
    }
205 8
206
    $tables  = AuditDataLayer::$dl->getTablesNames($this->config->getManString('database.audit_schema'));
207
    $tables3 = [];
208 8
    foreach ($tables as $table)
209
    {
210
      $tables3[] = $table['table_name'];
211
    }
212
213
    return array_intersect($tables1, $tables2, $tables3);
214
  }
215
216
  //--------------------------------------------------------------------------------------------------------------------
217
  /**
218
   * Returns the metadata of a table.
219
   *
220 8
   * @param string $schemaName The name of the schema of the table.
221
   * @param string $tableName  The name of the table.
222 8
   *
223 8
   * @return TableMetadata
224
   */
225 8
  private function getTableMetadata(string $schemaName, string $tableName): TableMetadata
226
  {
227
    $table   = AuditDataLayer::$dl->getTableOptions($schemaName, $tableName);
228
    $columns = AuditDataLayer::$dl->getTableColumns($schemaName, $tableName);
229
230
    return new TableMetadata($table, new TableColumnsMetadata($columns));
231
  }
232
233
  //--------------------------------------------------------------------------------------------------------------------
234
}
235
236
//----------------------------------------------------------------------------------------------------------------------
237