Completed
Push — master ( 08dfad...a46189 )
by P.R.
04:31
created

AlterAuditTable::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 5
nc 1
nop 1
dl 0
loc 8
ccs 6
cts 6
cp 1
crap 1
rs 10
c 0
b 0
f 0
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 $additionalAuditColumns;
25
26
  /**
27
   * Code store for alter table statement.
28
   *
29
   * @var AlterTableCodeStore
30
   */
31
  private $codeStore;
32
33
  /**
34
   * The strong typed configuration reader and writer.
35
   *
36
   * @var TypedConfig
37
   */
38
  private $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::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 create 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 counter part 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()->makeNullable();
86 8
    $dataTable->getColumns()->prependTableColumns($this->additionalAuditColumns);
87
88 8
    $this->compareTableOptions($dataTable, $auditTable);
89 8
    $this->compareTableColumns($dataTable, $auditTable);
90 8
  }
91
92
  //--------------------------------------------------------------------------------------------------------------------
93
  /**
94
   * Compares the columns of the data and audit tables and generates the appropriate alter table statement.
95
   *
96
   * @param TableMetadata $dataTable  The metadata of the data table.
97
   * @param TableMetadata $auditTable The metadata of the audit table.
98
   */
99 8
  private function compareTableColumns(TableMetadata $dataTable, TableMetadata $auditTable): void
100
  {
101 8
    $diff = TableColumnsMetadata::differentColumnTypes($dataTable->getColumns(), $auditTable->getColumns());
102
103 8
    if (!empty($diff->getColumns()))
104
    {
105 5
      $maxLength = $diff->getLongestColumnNameLength();
106
107 5
      $this->codeStore->append(sprintf('alter table `%s`.`%s`',
108 5
                                       $this->config->getManString('database.audit_schema'),
109 5
                                       $auditTable->getTableName()));
110
111 5
      $first = true;
112 5
      foreach ($diff->getColumns() as $column)
113
      {
114 5
        $name   = $column->getName();
115 5
        $filler = str_repeat(' ', $maxLength - mb_strlen($name) + 1);
116
117 5
        if (!$first) $this->codeStore->appendToLastLine(',');
118
119 5
        $this->codeStore->append(sprintf('change column `%s`%s`%s`%s%s',
120
                                         $name,
121
                                         $filler,
122
                                         $name,
123
                                         $filler,
124 5
                                         $column->getColumnAuditDefinition()));
125
126 5
        $first = false;
127
      }
128
129 5
      $this->codeStore->append(';');
130 5
      $this->codeStore->append('');
131
    }
132 8
  }
133
134
  //--------------------------------------------------------------------------------------------------------------------
135
  /**
136
   * Compares the table options of the data and audit tables and generates the appropriate alter table statement.
137
   *
138
   * @param TableMetadata $dataTable  The metadata of the data table.
139
   * @param TableMetadata $auditTable The metadata of the audit table.
140
   */
141 8
  private function compareTableOptions(TableMetadata $dataTable, TableMetadata $auditTable): void
142
  {
143 8
    $options = TableMetadata::compareOptions($dataTable, $auditTable);
144
145 8
    if (!empty($options))
146
    {
147 4
      $parts = [];
148 4
      foreach ($options as $option)
149
      {
150 4
        switch ($option)
151
        {
152 4
          case 'engine':
153 2
            $parts[] = 'engine '.$dataTable->getProperty('engine');
154 2
            break;
155
156 3
          case 'character_set_name':
157 2
            $parts[] = 'default character set '.$dataTable->getProperty('character_set_name');
158 2
            break;
159
160 3
          case 'table_collation':
161 3
            $parts[] = 'default collate '.$dataTable->getProperty('table_collation');
162 3
            break;
163
164
          default:
165
            throw new FallenException('option', $option);
166
        }
167
      }
168
169 4
      $this->codeStore->append(sprintf('alter table `%s`.`%s` %s;',
170 4
                                       $this->config->getManString('database.audit_schema'),
171 4
                                       $auditTable->getTableName(),
172 4
                                       implode(' ', $parts)));
173 4
      $this->codeStore->append('');
174
    }
175 8
  }
176
177
  //--------------------------------------------------------------------------------------------------------------------
178
  /**
179
   * Returns the names of the tables that must be compared.
180
   *
181
   * @return string[]
182
   */
183 8
  private function getTableList(): array
184
  {
185 8
    $tables1 = [];
186 8
    foreach ($this->config->getManArray('tables') as $tableName => $config)
187
    {
188 8
      if ($config['audit'])
189
      {
190 8
        $tables1[] = $tableName;
191
      }
192
    }
193
194 8
    $tables  = AuditDataLayer::getTablesNames($this->config->getManString('database.data_schema'));
195 8
    $tables2 = [];
196 8
    foreach ($tables as $table)
197
    {
198 8
      $tables2[] = $table['table_name'];
199
    }
200
201 8
    $tables  = AuditDataLayer::getTablesNames($this->config->getManString('database.audit_schema'));
202 8
    $tables3 = [];
203 8
    foreach ($tables as $table)
204
    {
205 8
      $tables3[] = $table['table_name'];
206
    }
207
208 8
    return array_intersect($tables1, $tables2, $tables3);
209
  }
210
211
  //--------------------------------------------------------------------------------------------------------------------
212
  /**
213
   * Returns the metadata of a table.
214
   *
215
   * @param string $schemaName The name of the schema of the table.
216
   * @param string $tableName  The name of the table.
217
   *
218
   * @return TableMetadata
219
   */
220 8
  private function getTableMetadata(string $schemaName, string $tableName): TableMetadata
221
  {
222 8
    $table   = AuditDataLayer::getTableOptions($schemaName, $tableName);
223 8
    $columns = AuditDataLayer::getTableColumns($schemaName, $tableName);
224
225 8
    return new TableMetadata($table, new TableColumnsMetadata($columns));
226
  }
227
228
  //--------------------------------------------------------------------------------------------------------------------
229
}
230
231
//----------------------------------------------------------------------------------------------------------------------
232