CreateAuditTrigger::createInsertStatementInto()   A
last analyzed

Complexity

Conditions 5
Paths 9

Size

Total Lines 25
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 5

Importance

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