-
Notifications
You must be signed in to change notification settings - Fork 37
/
Copy pathTablePersister.php
205 lines (185 loc) · 6.46 KB
/
TablePersister.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
<?php
declare(strict_types=1);
namespace AuditStash\Persister;
use AuditStash\PersisterInterface;
use Cake\Core\InstanceConfigTrait;
use Cake\ORM\Locator\LocatorAwareTrait;
use Cake\ORM\Table;
use InvalidArgumentException;
/**
* A persister that uses the ORM API to persist audit logs.
*/
class TablePersister implements PersisterInterface
{
use ExtractionTrait;
use InstanceConfigTrait;
use LocatorAwareTrait;
use LogTrait;
/**
* Strategy that will choose between raw and serialized.
*
* @var string
*/
public const STRATEGY_AUTOMATIC = 'automatic';
/**
* Strategy that extracts data as separate fields/properties.
*
* @var string
*/
public const STRATEGY_PROPERTIES = 'properties';
/**
* Strategy that extracts data as is.
*
* @var string
*/
public const STRATEGY_RAW = 'raw';
/**
* Strategy that extracts data serialized in JSON format.
*
* @var string
*/
public const STRATEGY_SERIALIZED = 'serialized';
/**
* The default configuration.
*
* ## Configuration options
*
* - `extractMetaFields` (`bool|array`, defaults to `false`)
*
* Defines whether/which meta data fields to extract as entity properties, thus
* making them savable in the target table. When set to `true`, all meta data fields
* are being extracted, where the keys are being used as the property/column names.
*
* When passing an array, a `key => value` map is expected, where the key should be
* a `Hash::get()` compatible path that is used to extract from the meta data, and
* the value will be used as the property/column named:
*
* ```
* [
* 'user.id' => 'user_id'
* ]
* ```
*
* This would extract `['user']['id']` from the metadata array, and pass it as
* a field named `user_id` to the entity to persist.
*
* Alternatively a flat array with single string values can be passed, where the value
* will be used as the property/name as well as the path for extracting.
*
* - `logErrors` (`bool`, defaults to `true`)
*
* Defines whether to log errors. By default, errors are logged using the log level
* `LogLevel::ERROR`.
*
* - `primaryKeyExtractionStrategy` (`string`, defaults to `TablePersister::STRATEGY_AUTOMATIC`)
*
* Defines how the primary key values of the changed records should be stored. Valid
* values are the persister class' `STORAGE_STRATEGY_*` constants:
*
* * `STRATEGY_AUTOMATIC`: Stores the primary key in a single column, either as is,
* or in case of a composite key, in JSON format.
*
* * `STRATEGY_PROPERTIES`: Stores the keys in individual columns if required.
* Composite primary keys will be stored prefixed with `primary_key_` followed by the
* index of the value in the primary key array.
*
* * `STRATEGY_RAW`: Stores the key as is in a single column.
*
* * `STRATEGY_SERIALIZED`: Stores the primary key in JSON format.
*
* - `serializeFields` (`bool`, defaults to `true`)
*
* Defines whether the (non-primary key) fields that expect array data are being
* serialized in JSON format.
*
* - `table` (`string|\Cake\ORM\Table`, defaults to `AuditLogs`)
*
* Defines the table to use for persisting records. Either a string denoting a table
* alias that is going to be resolved using the persisters table locator, or a table
* object.
*
* - `unsetExtractedMetaFields` (`bool`, defaults to `true`)
*
* Defines whether the fields extracted from the meta data should be unset, ie removed.
*
* @var array
*/
protected array $_defaultConfig = [
'extractMetaFields' => false,
'logErrors' => true,
'primaryKeyExtractionStrategy' => self::STRATEGY_AUTOMATIC,
'serializeFields' => true,
'table' => 'AuditLogs',
'unsetExtractedMetaFields' => true,
];
/**
* The table to use for persisting logs.
*
* @var \Cake\ORM\Table|null
*/
protected ?Table $_table = null;
/**
* Returns the table to use for persisting logs.
*
* @return \Cake\ORM\Table
*/
public function getTable(): Table
{
if ($this->_table === null) {
$this->setTable($this->getConfig('table'));
}
return $this->_table;
}
/**
* Sets the table to use for persisting logs.
*
* @param \Cake\ORM\Table|string|null $table Either a string denoting a table alias, or a table object.
* @return $this
*/
public function setTable(string|Table|null $table)
{
if (is_string($table)) {
$table = $this->getTableLocator()->get($table);
}
if (!($table instanceof Table)) {
throw new InvalidArgumentException(
'The `$table` argument must be either a table alias, or an instance of `\Cake\ORM\Table`.'
);
}
$this->_table = $table;
return $this;
}
/**
* Persists each of the passed EventInterface objects.
*
* @param array<\AuditStash\EventInterface> $auditLogs List of EventInterface objects to persist
* @return void
* @throws \Exception
*/
public function logEvents(array $auditLogs): void
{
$persisterTable = $this->getTable();
$serializeFields = $this->getConfig('serializeFields');
$primaryKeyExtractionStrategy = $this->getConfig('primaryKeyExtractionStrategy');
$extractMetaFields = $this->getConfig('extractMetaFields');
$unsetExtractedMetaFields = $this->getConfig('unsetExtractedMetaFields');
$logErrors = $this->getConfig('logErrors');
foreach ($auditLogs as $log) {
$fields = $this->extractBasicFields($log, $serializeFields);
$fields += $this->extractPrimaryKeyFields($log, $primaryKeyExtractionStrategy);
$fields += $this->extractMetaFields(
$log,
$extractMetaFields,
$unsetExtractedMetaFields,
$serializeFields
);
$persisterEntity = $persisterTable->newEntity($fields);
if (
!$persisterTable->save($persisterEntity) &&
$logErrors
) {
$this->log($this->toErrorLog($persisterEntity));
}
}
}
}