Passed
Push — master ( 9e86dc...6cbe7e )
by P.R.
06:56
created

CreateAuditTrigger::createInsertStatementValues()   B

Complexity

Conditions 9
Paths 25

Size

Total Lines 45
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 21
CRAP Score 9.0533

Importance

Changes 4
Bugs 0 Features 0
Metric Value
cc 9
eloc 25
c 4
b 0
f 0
nc 25
nop 1
dl 0
loc 45
ccs 21
cts 23
cp 0.913
crap 9.0533
rs 8.0555
1
<?php
2
declare(strict_types=1);
3
4
namespace SetBased\Audit\MySql\Sql;
5
6
use SetBased\Audit\Metadata\TableColumnsMetadata;
7
use SetBased\Audit\MySql\AuditDataLayer;
8
use SetBased\Exception\FallenException;
9
use SetBased\Exception\RuntimeException;
10
use SetBased\Helper\CodeStore\MySqlCompoundSyntaxCodeStore;
11
12
/**
13
 * Class for creating SQL statements for creating audit triggers.
14
 */
15
class CreateAuditTrigger
16
{
17
  //--------------------------------------------------------------------------------------------------------------------
18
  /**
19
   * The metadata of the additional audit columns.
20
   *
21
   * @var TableColumnsMetadata
22
   */
23
  private $additionalAuditColumns;
24
25
  /**
26
   * Additional SQL statements.
27
   *
28
   * @var string[]|null
29
   */
30
  private $additionalSql;
31
32
  /**
33
   * The name of the audit schema.
34
   *
35
   * @var string
36
   */
37
  private $auditSchemaName;
38
39
  /**
40
   * The generated code.
41
   *
42
   * @var MySqlCompoundSyntaxCodeStore
43
   */
44
  private $code;
45
46
  /**
47
   * The name of the data schema.
48
   *
49
   * @var string
50
   */
51
  private $dataSchemaName;
52
53
  /**
54
   * The skip variable.
55
   *
56
   * @var string|null
57
   */
58
  private $skipVariable;
59
60
  /**
61
   * Audit columns from metadata.
62
   *
63
   * @var TableColumnsMetadata
64
   */
65
  private $tableColumns;
66
67
  /**
68
   * The name of the data table.
69
   *
70
   * @var string
71
   */
72
  private $tableName;
73
74
  /**
75
   * The trigger action (i.e. INSERT, UPDATE, or DELETE).
76
   *
77
   * @var string
78
   */
79
  private $triggerAction;
80
81
  /**
82
   * The name of the trigger.
83
   *
84
   * @var string
85
   */
86
  private $triggerName;
87
88
  //--------------------------------------------------------------------------------------------------------------------
89
  /**
90
   * Creates a trigger on a table.
91
   *
92
   * @param string               $dataSchemaName         The name of the data schema.
93
   * @param string               $auditSchemaName        The name of the audit schema.
94
   * @param string               $tableName              The name of the table.
95
   * @param string               $triggerAction          The trigger action (i.e. INSERT, UPDATE, or DELETE).
96
   * @param string               $triggerName            The name of the trigger.
97
   * @param TableColumnsMetadata $additionalAuditColumns The metadata of the additional audit columns.
98
   * @param TableColumnsMetadata $tableColumns           The metadata of the data table columns.
99
   * @param string|null          $skipVariable           The skip variable.
100
   * @param string[]|null        $additionalSql          Additional SQL statements.
101
   */
102 31
  public function __construct(string $dataSchemaName,
103
                              string $auditSchemaName,
104
                              string $tableName,
105
                              string $triggerName,
106
                              string $triggerAction,
107
                              TableColumnsMetadata $additionalAuditColumns,
108
                              TableColumnsMetadata $tableColumns,
109
                              ?string $skipVariable,
110
                              ?array $additionalSql)
111
  {
112 31
    $this->dataSchemaName         = $dataSchemaName;
113 31
    $this->auditSchemaName        = $auditSchemaName;
114 31
    $this->tableName              = $tableName;
115 31
    $this->triggerName            = $triggerName;
116 31
    $this->triggerAction          = $triggerAction;
117 31
    $this->skipVariable           = $skipVariable;
118 31
    $this->additionalAuditColumns = $additionalAuditColumns;
119 31
    $this->tableColumns           = $tableColumns;
120 31
    $this->additionalSql          = $additionalSql;
121 31
  }
122
123
  //--------------------------------------------------------------------------------------------------------------------
124
  /**
125
   * Returns the SQL code for creating an audit trigger.
126
   *
127
   * @return string
128
   *
129
   * @throws FallenException
130
   */
131 31
  public function buildStatement(): string
132
  {
133 31
    $this->code = new MySqlCompoundSyntaxCodeStore();
134
135 31
    $rowState = [];
136 31
    switch ($this->triggerAction)
137
    {
138 31
      case 'INSERT':
139 31
        $rowState[] = 'NEW';
140 31
        break;
141
142 31
      case 'DELETE':
143 31
        $rowState[] = 'OLD';
144 31
        break;
145
146 31
      case 'UPDATE':
147 31
        $rowState[] = 'OLD';
148 31
        $rowState[] = 'NEW';
149 31
        break;
150
151
      default:
152
        throw new FallenException('action', $this->triggerAction);
153
    }
154
155 31
    $this->code->append(sprintf('create trigger `%s`.`%s`', $this->dataSchemaName, $this->triggerName));
156 31
    $this->code->append(sprintf('after %s on `%s`.`%s`',
157 31
                                strtolower($this->triggerAction),
158 31
                                $this->dataSchemaName,
159 31
                                $this->tableName));
160 31
    $this->code->append('for each row');
161 31
    $this->code->append('begin');
162
163 31
    if ($this->skipVariable!==null) $this->code->append(sprintf('if (%s is null) then', $this->skipVariable));
164
165 31
    $this->code->append($this->additionalSql);
166
167 31
    $this->createInsertStatement($rowState[0]);
168 31
    if (count($rowState)==2)
169
    {
170 31
      $this->createInsertStatement($rowState[1]);
171
    }
172
173 31
    if ($this->skipVariable!==null) $this->code->append('end if;');
174 31
    $this->code->append('end');
175
176 31
    return $this->code->getCode();
177
  }
178
179
  //--------------------------------------------------------------------------------------------------------------------
180
  /**
181
   * Adds an insert SQL statement to SQL code for a trigger.
182
   *
183
   * @param string $rowState The row state (i.e. OLD or NEW).
184
   */
185 31
  private function createInsertStatement(string $rowState): void
186
  {
187 31
    $this->createInsertStatementInto();
188 31
    $this->createInsertStatementValues($rowState);
189 31
  }
190
191
  //--------------------------------------------------------------------------------------------------------------------
192
  /**
193
   * Adds the "insert into" part of an insert SQL statement to SQL code for a trigger.
194
   */
195 31
  private function createInsertStatementInto(): void
196
  {
197 31
    $columnNames = '';
198
199
    // First the audit columns.
200 31
    foreach ($this->additionalAuditColumns->getColumns() as $column)
201
    {
202 20
      if ($columnNames) $columnNames .= ',';
203 20
      $columnNames .= sprintf('`%s`', $column->getName());
204
    }
205
206
    // Second the audit columns.
207 31
    foreach ($this->tableColumns->getColumns() as $column)
208
    {
209 31
      if ($columnNames) $columnNames .= ',';
210 31
      $columnNames .= sprintf('`%s`', $column->getName());
211
    }
212
213 31
    $this->code->append(sprintf('insert into `%s`.`%s`(%s)', $this->auditSchemaName, $this->tableName, $columnNames));
214 31
  }
215
216
  //--------------------------------------------------------------------------------------------------------------------
217
  /**
218
   * Adds the "values" part of an insert SQL statement to SQL code for a trigger.
219
   *
220
   * @param string $rowState The row state (i.e. OLD or NEW).
221
   */
222 31
  private function createInsertStatementValues(string $rowState): void
223
  {
224 31
    $values = '';
225
226
    // First the values for the audit columns.
227 31
    foreach ($this->additionalAuditColumns->getColumns() as $column)
228
    {
229 20
      $column = $column->getProperties();
230 20
      if ($values) $values .= ',';
231
232
      switch (true)
233
      {
234 20
        case (isset($column['value_type'])):
235 19
          switch ($column['value_type'])
236
          {
237 19
            case 'ACTION':
238 19
              $values .= AuditDataLayer::$dl->quoteString($this->triggerAction);
239 19
              break;
240
241 19
            case 'STATE':
242 19
              $values .= AuditDataLayer::$dl->quoteString($rowState);
243 19
              break;
244
245
            default:
246
              throw new FallenException('value_type', ($column['value_type']));
247
          }
248 19
          break;
249
250 11
        case (isset($column['expression'])):
251 11
          $values .= $column['expression'];
252 11
          break;
253
254
        default:
255
          throw new RuntimeException('None of value_type and expression are set.');
256
      }
257
    }
258
259
    // Second the values for the audit columns.
260 31
    foreach ($this->tableColumns->getColumns() as $column)
261
    {
262 31
      if ($values) $values .= ',';
263 31
      $values .= sprintf('%s.`%s`', $rowState, $column->getName());
264
    }
265
266 31
    $this->code->append(sprintf('values(%s);', $values));
267 31
  }
268
269
  //--------------------------------------------------------------------------------------------------------------------
270
}
271
272
//----------------------------------------------------------------------------------------------------------------------
273