ChangelogBehavior::implementedEvents()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 5
nc 1
nop 0
1
<?php
2
3
namespace Changelog\Model\Behavior;
4
5
use ArrayObject;
6
use Cake\Database\Type;
7
use Cake\Datasource\EntityInterface;
8
use Cake\Event\Event;
9
use Cake\ORM\Association;
10
use Cake\ORM\Association\BelongsTo;
11
use Cake\ORM\Behavior;
12
use Cake\ORM\Locator\LocatorAwareTrait;
13
use Cake\ORM\Table;
14
use Cake\Utility\Hash;
15
use Exception;
16
use UnexpectedValueException;
17
18
/**
19
 * TODO: impl
20
 *
21
 * Licensed under The MIT License
22
 * For full copyright and license information, please see the LICENSE file
23
 */
24
class ChangelogBehavior extends Behavior
25
{
26
27
    use LocatorAwareTrait;
28
29
    /**
30
     * Default config
31
     *
32
     * These are merged with user-provided configuration when the behavior is used.
33
     *
34
     * @var array
35
     */
36
    protected $_defaultConfig = [
37
        'autoSave' => true,
38
        'changelogTable' => 'Changelog.Changelogs',
39
        'columnTable' => 'Changelog.ChangelogColumns',
40
        'collectChangesOnBeforeSave' => true,
41
        'combinations' => [],
42
        'convertAssociations' => true,
43
        'convertForeignKeys' => true,
44
        'convertDatetimeColumns' => true,
45
        'equalComparison' => true,
46
        'exchangeForeignKey' => true,
47
        'filterForeignKeys' => true,
48
        'ignoreColumns' => [
49
            'id',
50
            'created',
51
            'modified'
52
        ],
53
        'logIsNew' => false,
54
        'logEmptyChanges' => false,
55
        'saveChangelogOnAfterSave' => true,
56
        'tableLocator' => null
57
    ];
58
59
    /**
60
     * Holds collected before values
61
     *
62
     * @var array
63
     */
64
    protected $_collectedBeforeValues = null;
65
66
    /**
67
     * Holds changes to save
68
     *
69
     * @var array
70
     */
71
    protected $_changes = [];
72
73
    /**
74
     * olds combination columns
75
     *
76
     * @var array
77
     */
78
    protected $_combinationColumns = [];
79
80
    /**
81
     * Initialize tableLocator also.
82
     *
83
     * {@inheritdoc}
84
     */
85
    public function initialize(array $config = [])
86
    {
87
        parent::initialize($config);
88
        if ($tableLocator = $this->config('tableLocator')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
89
            $this->tableLocator($tableLocator);
90
        }
91
    }
92
93
    /**
94
     * beforeSave callback.
95
     * Collects original values of associations.
96
     *
97
     * {@inheritdoc}
98
     */
99
    public function beforeSave(Event $event, EntityInterface $entity, ArrayObject $options)
0 ignored issues
show
Unused Code introduced by
The parameter $event is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
100
    {
101
        if ($this->config('collectChangesOnBeforeSave')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
102
            $this->collectChanges($entity, $options);
103
        }
104
    }
105
106
    public function collectChanges(EntityInterface $entity, ArrayObject $options = null)
107
    {
108
        $Changelogs = $this->getChangelogTable();
0 ignored issues
show
Unused Code introduced by
$Changelogs is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
109
        $Columns = $this->getColumnTable();
110
        $table = $this->_table;
111
112
        /**
113
         * Be sure whether log new entity or not.
114
         */
115
        if (!$this->config('logIsNew') && $entity->isNew()) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
116
            return false;
117
        }
118
119
        /**
120
         * Extract column names from original values
121
         */
122
        $columns = array_keys($entity->getOriginalValues());
123
124
        /**
125
         * Extract dirty columns.
126
         * Exchange columns to actually dirty ones.
127
         */
128
        $afterValues = $entity->extract($columns, $isDirty = true);
129
        $columns = array_keys($afterValues);
130
131
        /**
132
         * Adds extra columns when combinations was given.
133
         */
134
        $this->_combinationColumns = [];
135
        if ($combinations = $this->config('combinations')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
136
            foreach ($combinations as $name => $settings) {
137
                $settings = $this->_normalizeCombinationSettings($settings);
138
                $this->_combinationColumns = array_merge($this->_combinationColumns, $settings['columns']);
139
            }
140
            $this->_combinationColumns = array_values(array_unique($this->_combinationColumns));
141
142
            $columns = array_values(array_unique(array_merge($columns, $this->_combinationColumns)));
143
            $afterValues = $entity->extract($columns);
144
        }
145
146
        /**
147
         * Extract original value from decided columns.
148
         */
149
        $beforeValues = $entity->extractOriginal($columns);
0 ignored issues
show
Bug introduced by
The method extractOriginal() does not exist on Cake\Datasource\EntityInterface. Did you maybe mean extract()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
150
151
        /**
152
         * Filters ignored columns
153
         */
154
        $columns = array_diff($columns, $this->config('ignoreColumns'));
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
155
156
        /**
157
         * Exception, before counts should equal to after counts
158
         */
159
        if (count($beforeValues) !== count($afterValues)) {
160
            return false;
161
        }
162
163
        /**
164
         * Filters changes
165
         */
166
        $changes = [];
167
        $associations = $this->_associationsIndexedByProperty();
168
        $foreignKeys = $this->_associationsForForeignKey();
169
        foreach ($columns as $column) {
170
            /**
171
             * Prepare values for events
172
             */
173
            $before = $beforeValues[$column];
174
            $after = $afterValues[$column];
175
176
            $isAssociation = array_key_exists($column, $associations);
177
            $isForeignKey = array_key_exists($column, $foreignKeys);
178
179
            /**
180
             * Prepare association/column info
181
             */
182
            if ($isAssociation) {
183
                $columnDef = null;
184
                $association = $associations[$column];
185
            } else {
186
                $columnDef = $table->schema()->column($column);
0 ignored issues
show
Deprecated Code introduced by
The method Cake\ORM\Table::schema() has been deprecated with message: 3.4.0 Use setSchema()/getSchema() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
187
                $association = null;
188
                if ($isForeignKey) {
189
                    $association = $foreignKeys[$column];
190
                }
191
            }
192
193
            /**
194
             * Event data. These variables can be changed via registered events.
195
             */
196
            $eventData = new ArrayObject(compact([
197
                'entity',
198
                'isForeignKey',
199
                'isAssociation',
200
                'before',
201
                'after',
202
                'beforeValues',
203
                'afterValues',
204
                'column',
205
                'columnDef',
206
                'association',
207
                'table',
208
                'Columns'
209
            ]));
210
211
            /**
212
             * Dispatches convert event
213
             */
214
            $event = $table->dispatchEvent('Changelog.convertValues', [$eventData]);
0 ignored issues
show
Unused Code introduced by
$event is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
215
            extract((array)$eventData);
0 ignored issues
show
Bug introduced by
(array) $eventData cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
216
217
            if (!$this->_combinationColumns || !in_array($column, $this->_combinationColumns)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->_combinationColumns of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
218
                /**
219
                 * Dispatches filter event
220
                 */
221
                $event = $table->dispatchEvent('Changelog.filterChanges', [$eventData]);
222
                if (!$event->result) {
223
                    continue;
224
                }
225
            }
226
227
            /**
228
             * Determine changes from result
229
             */
230
            extract((array)$eventData);
0 ignored issues
show
Bug introduced by
(array) $eventData cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
231
            $changes[] = [
232
                'column' => $column,
233
                'before' => $before,
234
                'after' => $after,
235
            ];
236
        }
237
238
        /**
239
         * Make combinations
240
         */
241
        if ($combinations = $this->config('combinations')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
242
            $changes = $this->makeChangelogCombinations($entity, $changes, $combinations);
243
        }
244
245
        return $this->_changes = $changes;
246
    }
247
248
    public function makeChangelogCombinations(EntityInterface $entity, array $changes, array $combinations)
249
    {
250
        $indexedByColumn = collection($changes)->indexBy('column')->toArray();
251
        $removeKeys = [];
252
        $foreignKeys = $this->_associationsForForeignKey();
253
        foreach ($combinations as $name => $settings) {
254
            $settings = $this->_normalizeCombinationSettings($settings);
255
256
            $values = [];
257
            foreach ($settings['columns'] as $column) {
258
                $removeKeys[] = $column;
259
260
                if ($association = Hash::get($foreignKeys, $column)) {
261
                    $property = $association->property();
262
                    if (isset($indexedByColumn[$property])) {
263
                        $values['before'][$column] = $indexedByColumn[$property]['before'];
264
                        $values['after'][$column] = $indexedByColumn[$property]['after'];
265
                        $removeKeys[] = $property;
266
                    } elseif (isset($indexedByColumn[$column])) {
267
                        $values['before'][$column] = $indexedByColumn[$column]['before'];
268
                        $values['after'][$column] = $indexedByColumn[$column]['after'];
269
                    } else {
270
                        $values['before'][$column] = $entity->get($column);
271
                        $values['after'][$column] = $entity->get($column);
272
                    }
273
                } elseif (isset($indexedByColumn[$column])) {
274
                    $values['before'][$column] = $indexedByColumn[$column]['before'];
275
                    $values['after'][$column] = $indexedByColumn[$column]['after'];
276
                } else {
277
                    $values['before'][$column] = $entity->get($column);
278
                    $values['after'][$column] = $entity->get($column);
279
                }
280
            }
281
282
            if (isset($settings['convert'])) {
283
                $convert = $settings['convert'];
284
                $converted = $convert($name, $values['before'], $values['after']);
285
            } else {
286
                $converted = [
287
                    'column' => $name,
288
                    'before' => implode(' ', array_filter($values['before'])),
289
                    'after' => implode(' ', array_filter($values['after'])),
290
                ];
291
            }
292
293
            /**
294
             * Filters converted value not equal to. also remove keys from indexedColumn
295
             * to prevent changes remaining.
296
             */
297
            if ($converted['before'] == $converted['after']) {
298
                unset($combinations[$name]);
299
                continue;
300
            }
301
            $indexedByColumn[$name] = $converted;
302
        }
303
304
        $removeKeys = array_diff($removeKeys, array_keys($combinations));
305
        $indexedByColumn = array_diff_key($indexedByColumn, array_flip($removeKeys));
306
        return array_values($indexedByColumn);
307
    }
308
309
    protected function _normalizeCombinationSettings($settings)
310
    {
311
        if (!is_array($settings)) {
312
            throw new UnexpectedValueException(__d('changelog', 'Changelog: `combinations` option should be array'));
313
        }
314
315
        /**
316
         * If numric keys e.g. ['first_name', 'last_name'] given, Handles it
317
         * as a list of columns.
318
         */
319
        if (Hash::numeric(array_keys($settings))) {
320
            $settings = ['columns' => $settings];
321
        }
322
323
        if (!isset($settings['columns']) || !is_array($settings['columns'])) {
324
            throw new UnexpectedValueException(__d('changelog', 'Changelog: `combinations` option should have `columns` key and value as array of columns'));
325
        }
326
327
        return $settings;
328
    }
329
330
    /**
331
     * afterSave callback.
332
     * This logs entities when `onAfterSave` option was turned on.
333
     *
334
     * {@inheritdoc}
335
     */
336
    public function afterSave(Event $event, EntityInterface $entity, ArrayObject $options)
337
    {
338
        if ($this->config('saveChangelogOnAfterSave')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
339
            $this->saveChangelog($entity, $this->_changes);
340
        }
341
    }
342
343
    /**
344
     * Saves changelogs for entity.
345
     *
346
     * @param \Cake\Datasource\EntityInterface $entity The entity object to log changes.
347
     * @return \Cake\Datasource\EntityInterface|bool Entity object when logged otherwise `false`.
348
     */
349
    public function saveChangelog(EntityInterface $entity, $changes = [])
350
    {
351
        /**
352
         * Be sure whether change was done or not
353
         */
354
        if (!$this->config('logEmptyChanges') && empty($changes)) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
355
            return false;
356
        }
357
358
        /**
359
         * Saves actually
360
         */
361
        $data = new ArrayObject([
362
            'model' => $this->_table->alias(),
0 ignored issues
show
Deprecated Code introduced by
The method Cake\ORM\Table::alias() has been deprecated with message: 3.4.0 Use setAlias()/getAlias() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
363
            'foreign_key' => $entity->get($this->_table->primaryKey()),
0 ignored issues
show
Bug introduced by
It seems like $this->_table->primaryKey() targeting Cake\ORM\Table::primaryKey() can also be of type array; however, Cake\Datasource\EntityInterface::get() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Deprecated Code introduced by
The method Cake\ORM\Table::primaryKey() has been deprecated with message: 3.4.0 Use setPrimaryKey()/getPrimaryKey() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
364
            'is_new' => $entity->isNew(),
365
            'changelog_columns' => $changes,
366
        ]);
367
        $options = new ArrayObject([
368
            'associated' => 'ChangelogColumns',
369
            'atomic' => false
370
        ]);
371
372
        $Changelogs = $this->getChangelogTable();
373
        return $this->_table->dispatchEvent('Changelog.saveChangelogRecords', compact('Changelogs', 'data', 'options'))->result;
374
    }
375
376
    /**
377
     * Helper method to get table associations array
378
     * indexed by these properties.
379
     *
380
     * @return \Cake\ORM\Association[]
381
     */
382
    protected function _associationsIndexedByProperty()
383
    {
384
        return collection($this->_table->associations())
385
            ->combine(function ($association) {
386
                return $association->property();
387
            }, function ($association) {
388
                return $association;
389
            })->toArray();
390
    }
391
392
    /**
393
     * Helper method to get associations array that table has
394
     * foreign key (means BelongsTo) indexed by foreign key.
395
     *
396
     * @return \Cake\ORM\Association[]
397
     */
398
    protected function _associationsForForeignKey()
399
    {
400
        return collection($this->_table->associations())
401
            ->filter(function ($association) {
402
                return $association instanceof BelongsTo;
403
            })->combine(function ($association) {
404
                return $association->foreignKey();
405
            }, function ($association) {
406
                return $association;
407
            })->toArray();
408
    }
409
410
    /**
411
     * Returns changelogs table
412
     *
413
     * @return \Cake\ORM\Table
414
     */
415
    public function getChangelogTable()
416
    {
417
        return $this->tableLocator()->get($this->config('changelogTable'));
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
418
    }
419
420
    /**
421
     * Returns changelogs table
422
     *
423
     * @return \Cake\ORM\Table
424
     */
425
    public function getColumnTable()
426
    {
427
        return $this->tableLocator()->get($this->config('columnTable'));
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
428
    }
429
430
    /**
431
     * Define additional events for filter
432
     *
433
     * {@inheritdoc}
434
     */
435
    public function implementedEvents()
436
    {
437
        return parent::implementedEvents() + [
438
            'Changelog.convertValues' => 'convertChangelogValues',
439
            'Changelog.filterChanges' => 'filterChanges',
440
            'Changelog.saveChangelogRecords' => 'saveChangelogRecords',
441
        ];
442
    }
443
444
    /**
445
     * Default convert process
446
     *
447
     * @param \Cake\Event\Event $event The event for callback
448
     * @param ArrayObject $data The event data. contains:
449
     *                          - entity
450
     *                          - isForeignKey
451
     *                          - isAssociation
452
     *                          - before
453
     *                          - after
454
     *                          - beforeValues
455
     *                          - afterValues
456
     *                          - column
457
     *                          - columnDef
458
     *                          - table
459
     *                          - Columns
460
     * @return array couple of $before, $after
461
     */
462
    public function convertChangelogValues(Event $event, ArrayObject $data)
463
    {
464
        extract((array)$data);
0 ignored issues
show
Bug introduced by
(array) $data cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
465
        /**
466
         * @var \Cake\ORM\Entity $entity
467
         * @var bool $isForeignKey
468
         * @var bool $isAssociation
469
         * @var mixed $before
470
         * @var mixed $after
471
         * @var array $beforeValues
472
         * @var array $afterValues
473
         * @var string $column
474
         * @var array $columnDef
475
         * @var \Cake\ORM\Table $table
476
         * @var \Cake\ORM\Table $Columns
477
         */
478
479
        /**
480
         * Date inputs sometime represents string value in
481
         * entity. This converts value for comparison.
482
         */
483
        if ($this->config('convertDatetimeColumns') && !$isAssociation && isset($columnDef['type'])) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
484
            switch ($columnDef['type']) {
485
                case 'date':
486
                case 'datetime':
487
                case 'time':
488
                // For Postgres
489
                case 'timestamp':
490
                    $baseType = $table->schema()->baseColumnType($column);
491
                    if ($baseType && Type::map($baseType)) {
492
                        $driver = $table->connection()->driver();
493
                        $before = Type::build($baseType)->toPHP($before, $driver);
494
                        $after = Type::build($baseType)->toPHP($after, $driver);
495
                    }
496
                    break;
497
            }
498
        }
499
500
        /**
501
         * Converts foreign keys. This converts belongsTo ID columns to associated
502
         * entity. Then it takes display field for the table.
503
         */
504
        if ($isForeignKey && $this->config('convertForeignKeys')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
505
            if ($this->config('exchangeForeignKey')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
506
                unset($data['beforeValues'][$column]);
507
                unset($data['afterValues'][$column]);
508
                $column = $association->property();
509
                $data['column'] = $column;
510
                $this->_combinationColumns[] = $column;
511
            }
512
513
            $before = $association->findById($before)->first(); 
514
            $after = $association->findById($after)->first();
515
            $before = $this->convertAssociationChangeValue($before, $association, 'before');
516
            $after = $this->convertAssociationChangeValue($after, $association, 'after');
517
        }
518
519
        /**
520
         * Converts associations
521
         */
522
        $converter = $this->config('convertAssociations');
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
523
        if ($isAssociation && $converter) {
524
            /** 
525
             * If array was given, handles it as whitelist of associations
526
             */
527
            if (!is_array($converter) || is_callable($converter) || in_array($column, $converter)) {
528
                $before = $this->convertAssociationChangeValue($before, $association, 'before');
529
                $after = $this->convertAssociationChangeValue($after, $association, 'after');
530
            }
531
        }
532
533
        /**
534
         * Modifies event data
535
         */
536
        $data['before'] = $before;
537
        $data['after'] = $after;
538
        $data['beforeValues'][$column] = $before;
539
        $data['afterValues'][$column] = $after;
540
    }
541
542
    /**
543
     * Default converter for association values
544
     */
545
    public function convertAssociationChangeValue($value, $association, $kind)
546
    {
547
        if (!$value) {
548
            return is_array($value) ? null : $value;
549
        }
550
551
        $isMany = in_array($association->type(), [Association::MANY_TO_MANY, Association::ONE_TO_MANY]);
552
        $property = $association->property();
553
554
        /**
555
         * Call actual converter. callable can be set with `convertAssociations`
556
         * option.
557
         */
558
        $converter = $this->config('convertAssociations');
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
559
        $callable = is_callable($converter) ? $converter : [$this, 'defaultConvertAssociation'];
560
        $arguments = [$property, $value, $kind, $association, $isMany];
561
562
        return call_user_func_array($callable, $arguments);
563
    }
564
565
    /**
566
     * Default converter for association values.
567
     *
568
     * @param string $property association property name
569
     * @param mixed $value expects EntityInterface/EntityInterface[]
570
     * @param string $kind either 'before'/'after'
571
     * @param \Cake\ORM\Association $association association object for the value
572
     * @param boolean $isMany true => [hasMany, belongsToMany] false => [hasOne, belongsTo]
573
     * @param array $beforeValue association original values. indexed by association properties.
0 ignored issues
show
Bug introduced by
There is no parameter named $beforeValue. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
574
     * @return mixed converted value
575
     */
576
    public function defaultConvertAssociation($property, $value, $kind, $association, $isMany)
577
    {
578
        if (!$value) {
579
            return null;
580
        }
581
        $displayField = $association->displayField();
582
583
        // hasMany, belongsToMany
584
        if ($isMany) {
585
            $values = (array)$value;
586
            return implode(', ', collection($values)
587
                ->sortBy($displayField, SORT_ASC, SORT_NATURAL)
588
                ->extract($displayField)
589
                ->filter()
590
                ->toArray());
591
        // hasOne, belongsTo
592
        } else {
593
            if (!$value instanceof EntityInterface) {
594
                return $value;
595
            }
596
597
            if ($kind === 'before') {
598
                return $value->getOriginal($displayField);
599
            } else {
600
                return $value->get($displayField);
601
            }
602
        }
603
    }
604
605
    /**
606
     * Default filter
607
     *
608
     * @param \Cake\Event\Event $event The event for callback
609
     * @param ArrayObject $data The event data. contains:
610
     *                          - entity
611
     *                          - isForeignKey
612
     *                          - isAssociation
613
     *                          - before
614
     *                          - after
615
     *                          - beforeValues
616
     *                          - afterValues
617
     *                          - column
618
     *                          - columnDef
619
     *                          - table
620
     *                          - Columns
621
     * @return bool column is changed or not
622
     */
623
    public function filterChanges(Event $event, ArrayObject $data)
624
    {
625
        extract((array)$data);
0 ignored issues
show
Bug introduced by
(array) $data cannot be passed to extract() as the parameter $var_array expects a reference.
Loading history...
626
        /**
627
         * @var \Cake\ORM\Entity $entity
628
         * @var bool $isForeignKey
629
         * @var bool $isAssociation
630
         * @var mixed $before
631
         * @var mixed $after
632
         * @var array $beforeValues
633
         * @var array $afterValues
634
         * @var string $column
635
         * @var array $columnDef
636
         * @var \Cake\ORM\Table $table
637
         * @var \Cake\ORM\Table $Columns
638
         */
639
640
        /**
641
         * Filter e.g. null != ''
642
         */
643
        if ($this->config('equalComparison')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
644
            return $before != $after;
645
        }
646
647
        /**
648
         * Filter foreign keys
649
         */
650
        if ($this->config('filterForeignKeys')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
651
            return !$isForeignKey;
652
        }
653
654
        return true;
655
    }
656
657
    /**
658
     * Default save process
659
     *
660
     * @param \Cake\Event\Event $event The event for callback
661
     * @param \Cake\ORM\Table $Changelogs The table for parent
662
     * @param ArrayObject $data save data
663
     * @param ArrayObject $options save options
664
     * @return bool column is changed or not
665
     */
666
    public function saveChangelogRecords(Event $event, Table $Changelogs, ArrayObject $data, ArrayObject $options)
667
    {
668
        /**
669
         * Save changes to database
670
         */
671
        if ($this->config('autoSave')) {
0 ignored issues
show
Deprecated Code introduced by
The method Cake\Core\InstanceConfigTrait::config() has been deprecated with message: 3.4.0 use setConfig()/getConfig() instead.

This method has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the method will be removed from the class and what other method or class to use instead.

Loading history...
672
            $changelog = $Changelogs->newEntity((array)$data);
673
            return $Changelogs->save($changelog, (array)$options);
674
        }
675
    }
676
677
}
678