Completed
Push — master ( 041006...9a5cb2 )
by 紘己
01:56
created

ChangelogBehavior::convertChangelogValues()   D

Complexity

Conditions 18
Paths 120

Size

Total Lines 78
Code Lines 33

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 78
rs 4.7798
c 0
b 0
f 0
cc 18
eloc 33
nc 120
nop 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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
        'collectChangeOnBeforeSave' => 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
     * Default config
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
     * Initialize tableLocator also.
75
     *
76
     * {@inheritdoc}
77
     */
78
    public function initialize(array $config = [])
79
    {
80
        parent::initialize($config);
81
        if ($tableLocator = $this->config('tableLocator')) {
82
            $this->tableLocator($tableLocator);
83
        }
84
    }
85
86
    /**
87
     * beforeSave callback.
88
     * Collects original values of associations.
89
     *
90
     * {@inheritdoc}
91
     */
92
    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...
93
    {
94
        if ($this->config('collectChangeOnBeforeSave')) {
95
            $this->collectChanges($entity, $options);
96
        }
97
    }
98
99
    public function collectChanges(EntityInterface $entity, ArrayObject $options = null)
100
    {
101
        /**
102
         * Custom before values can be set via save options.
103
         */
104
        if ($options && isset($options['collectedBeforeValues'])) {
105
            $this->_collectedBeforeValues = $options['collectedBeforeValues'];
106
        } else {
107
            $this->collectChangelogBeforeValues($entity);
108
        }
109
110
        $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...
111
        $Columns = $this->getColumnTable();
112
        $table = $this->_table;
113
114
        /**
115
         * Be sure whether log new entity or not.
116
         */
117
        if (!$this->config('logIsNew') && $entity->isNew()) {
118
            return false;
119
        }
120
121
        /**
122
         * Extract column names from original values
123
         */
124
        $columns = array_keys($entity->getOriginalValues());
125
126
        /**
127
         * Extract dirty columns.
128
         * Exchange columns to actually dirty ones.
129
         */
130
        $afterValues = $entity->extract($columns, $isDirty = true);
131
        $columns = array_keys($afterValues);
132
133
        /**
134
         * Adds extra columns when combinations was given.
135
         */
136
        if ($combinations = $this->config('combinations')) {
137
            foreach ($combinations as $name => $settings) {
138
                $settings = $this->_normalizeCombinationSettings($settings);
139
                $columns = array_merge($columns, $settings['columns']);
140
            }
141
            $columns = array_values(array_unique($columns));
142
            $afterValues = $entity->extract($columns);
143
        }
144
145
        /**
146
         * Extract original value from decided columns.
147
         */
148
        $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...
149
150
        /**
151
         * Filters ignored columns
152
         */
153
        $columns = array_diff($columns, $this->config('ignoreColumns'));
154
155
        /**
156
         * Exception, before counts should equal to after counts
157
         */
158
        if (count($beforeValues) !== count($afterValues)) {
159
            return false;
160
        }
161
162
        /**
163
         * Filters changes
164
         */
165
        $changes = [];
166
        $associations = $this->_associationsIndexedByProperty();
167
        $foreignKeys = $this->_associationsForForeignKey();
168
        foreach ($columns as $column) {
169
            /**
170
             * Prepare values for events
171
             */
172
            $before = $beforeValues[$column];
173
            $after = $afterValues[$column];
174
175
            $isAssociation = array_key_exists($column, $associations);
176
            $isForeignKey = array_key_exists($column, $foreignKeys);
177
178
            /**
179
             * Prepare association/column info
180
             */
181
            if ($isAssociation) {
182
                $columnDef = null;
183
                $association = $associations[$column];
184
            } else {
185
                $columnDef = $table->schema()->column($column);
186
                $association = null;
187
                if ($isForeignKey) {
188
                    $association = $foreignKeys[$column];
189
                }
190
            }
191
192
            /**
193
             * Event data. These variables can be changed via registered events.
194
             */
195
            $eventData = new ArrayObject(compact([
196
                'entity',
197
                'isForeignKey',
198
                'isAssociation',
199
                'before',
200
                'after',
201
                'beforeValues',
202
                'afterValues',
203
                'column',
204
                'columnDef',
205
                'association',
206
                'table',
207
                'Columns'
208
            ]));
209
210
            /**
211
             * Dispatches convert event
212
             */
213
            $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...
214
215
            /**
216
             * Dispatches filter event
217
             */
218
            $event = $table->dispatchEvent('Changelog.filterChanges', [$eventData]);
219
            if (!$event->result) {
220
                continue;
221
            }
222
223
            /**
224
             * Determine changes from result
225
             */
226
            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...
227
            if ($event->result) {
228
                $changes[] = [
229
                    'column' => $column,
230
                    'before' => $before,
231
                    'after' => $after,
232
                ];
233
            }
234
        }
235
236
        /**
237
         * Make combinations
238
         */
239
        if ($combinations = $this->config('combinations')) {
240
            $changes = $this->makeChangelogCombinations($entity, $changes, $combinations);
241
        }
242
243
        return $this->_changes = $changes;
244
    }
245
246
    public function makeChangelogCombinations(EntityInterface $entity, array $changes, array $combinations)
247
    {
248
        $indexedByColumn = collection($changes)->indexBy('column')->toArray();
249
        $removeKeys = [];
250
        $foreignKeys = $this->_associationsForForeignKey();
251
        foreach ($combinations as $name => $settings) {
252
            $settings = $this->_normalizeCombinationSettings($settings);
253
254
            $values = [];
255
            foreach ($settings['columns'] as $column) {
256
                $removeKeys[] = $column;
257
                if (!isset($indexedByColumn[$column])) {
258
                    /**
259
                     * convert foreign keys
260
                     */
261
                    if (isset($foreignKeys[$column])) {
262
                        $association = $foreignKeys[$column];
263
                        $column = $association->property();
264
                        $values['before'][$column] = $indexedByColumn[$column]['after'];
265
                        $values['after'][$column] = $indexedByColumn[$column]['after'];
266
                        $removeKeys[] = $column;
267
                    } else {
268
                        $values['before'][$column] = $entity->get($column);
269
                        $values['after'][$column] = $entity->get($column);
270
                    }
271
                } else {
272
                    /**
273
                     * convert foreign keys
274
                     */
275
                    if (isset($foreignKeys[$column])) {
276
                        $values['before'][$column] = $indexedByColumn[$column]['after'];
277
                        $values['after'][$column] = $indexedByColumn[$column]['after'];
278
                    } else {
279
                        $values['before'][$column] = $indexedByColumn[$column]['before'];
280
                        $values['after'][$column] = $indexedByColumn[$column]['after'];
281
                    }
282
                }
283
            }
284
285
            if ($values['before'] == $values['after']) {
286
                continue;
287
            }
288
289
            if (isset($settings['convert'])) {
290
                $convert = $settings['convert'];
291
                $indexedByColumn[$name] = $convert($name, $values['before'], $values['after']);
292
            } else {
293
                $indexedByColumn[$name] = [
294
                    'column' => $name,
295
                    'before' => implode(' ', array_filter($values['before'])),
296
                    'after' => implode(' ', array_filter($values['after'])),
297
                ];
298
            }
299
        }
300
301
        $removeKeys = array_diff($removeKeys, array_keys($combinations));
302
        $indexedByColumn = array_diff_key($indexedByColumn, array_flip($removeKeys));
303
        return array_values($indexedByColumn);
304
    }
305
306
    protected function _normalizeCombinationSettings($settings)
307
    {
308
        if (!is_array($settings)) {
309
            throw new UnexpectedValueException(__d('changelog', 'Changelog: `combinations` option should be array'));
310
        }
311
312
        /**
313
         * If numric keys e.g. ['first_name', 'last_name'] given, Handles it
314
         * as a list of columns.
315
         */
316
        if (Hash::numeric(array_keys($settings))) {
317
            $settings = ['columns' => $settings];
318
        }
319
320
        if (!isset($settings['columns']) || !is_array($settings['columns'])) {
321
            throw new UnexpectedValueException(__d('changelog', 'Changelog: `combinations` option should have `columns` key and value as array of columns'));
322
        }
323
324
        return $settings;
325
    }
326
327
    public function collectChangelogBeforeValues($entity)
328
    {
329
        $this->_collectedBeforeValues = [];
330
        $associations = $this->_associationsIndexedByProperty();
331
        foreach ($entity->getOriginalValues() as $key => $value) {
332
            if (isset($associations[$key])) {
333
                $association = $associations[$key];
334
                if (in_array($association->type(), [Association::MANY_TO_MANY, Association::ONE_TO_MANY])) {
335
                    $values = (array)$value;
336
                    foreach ($values as $i => $v) {
337
                        if ($v instanceof EntityInterface) {
338
                            $values[$i] = $v->getOriginalValues();
339
                        } else {
340
                            $values[$i] = $v;
341
                        }
342
                    }
343
                    $this->_collectedBeforeValues[$key] = $values;
344
                } else {
345
                    if ($value instanceof EntityInterface) {
346
                        $this->_collectedBeforeValues[$key] = $value->getOriginalValues();
347
                    } else {
348
                        $this->_collectedBeforeValues[$key] = $value;
349
                    }
350
                }
351
            }
352
        }
353
354
        return $this->_collectedBeforeValues;
355
    }
356
357
    /**
358
     * afterSave callback.
359
     * This logs entities when `onAfterSave` option was turned on.
360
     *
361
     * {@inheritdoc}
362
     */
363
    public function afterSave(Event $event, EntityInterface $entity, ArrayObject $options)
364
    {
365
        if ($this->config('saveChangelogOnAfterSave')) {
366
            $this->saveChangelog($entity, $this->_changes);
367
        }
368
    }
369
370
    /**
371
     * Saves changelogs for entity.
372
     *
373
     * @param \Cake\Datasource\EntityInterface $entity The entity object to log changes.
374
     * @return \Cake\Datasource\EntityInterface|bool Entity object when logged otherwise `false`.
375
     */
376
    public function saveChangelog(EntityInterface $entity, $changes = [])
377
    {
378
        /**
379
         * Be sure whether change was done or not
380
         */
381
        if (!$this->config('logEmptyChanges') && empty($changes)) {
382
            return false;
383
        }
384
385
        /**
386
         * Saves actually
387
         */
388
        $data = new ArrayObject([
389
            'model' => $this->_table->alias(),
390
            '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...
391
            'is_new' => $entity->isNew(),
392
            'changelog_columns' => $changes,
393
        ]);
394
        $options = new ArrayObject([
395
            'associated' => 'ChangelogColumns',
396
            'atomic' => false
397
        ]);
398
399
        $Changelogs = $this->getChangelogTable();
400
        return $this->_table->dispatchEvent('Changelog.saveChangelogRecords', compact('Changelogs', 'data', 'options'))->result;
401
    }
402
403
    /**
404
     * Helper method to get table associations array
405
     * indexed by these properties.
406
     *
407
     * @return \Cake\ORM\Association[]
408
     */
409
    protected function _associationsIndexedByProperty()
410
    {
411
        return collection($this->_table->associations())
412
            ->combine(function ($association) {
413
                return $association->property();
414
            }, function ($association) {
415
                return $association;
416
            })->toArray();
417
    }
418
419
    /**
420
     * Helper method to get associations array that table has
421
     * foreign key (means BelongsTo) indexed by foreign key.
422
     *
423
     * @return \Cake\ORM\Association[]
424
     */
425
    protected function _associationsForForeignKey()
426
    {
427
        return collection($this->_table->associations())
428
            ->filter(function ($association) {
429
                return $association instanceof BelongsTo;
430
            })->combine(function ($association) {
431
                return $association->foreignKey();
432
            }, function ($association) {
433
                return $association;
434
            })->toArray();
435
    }
436
437
    /**
438
     * Returns changelogs table
439
     *
440
     * @return \Cake\ORM\Table
441
     */
442
    public function getChangelogTable()
443
    {
444
        return $this->tableLocator()->get($this->config('changelogTable'));
445
    }
446
447
    /**
448
     * Returns changelogs table
449
     *
450
     * @return \Cake\ORM\Table
451
     */
452
    public function getColumnTable()
453
    {
454
        return $this->tableLocator()->get($this->config('columnTable'));
455
    }
456
457
    /**
458
     * Define additional events for filter
459
     *
460
     * {@inheritdoc}
461
     */
462
    public function implementedEvents()
463
    {
464
        return parent::implementedEvents() + [
465
            'Changelog.convertValues' => 'convertChangelogValues',
466
            'Changelog.filterChanges' => 'filterChanges',
467
            'Changelog.saveChangelogRecords' => 'saveChangelogRecords',
468
        ];
469
    }
470
471
    /**
472
     * Default convert process
473
     *
474
     * @param \Cake\Event\Event $event The event for callback
475
     * @param ArrayObject $data The event data. contains:
476
     *                          - entity
477
     *                          - isForeignKey
478
     *                          - isAssociation
479
     *                          - before
480
     *                          - after
481
     *                          - beforeValues
482
     *                          - afterValues
483
     *                          - column
484
     *                          - columnDef
485
     *                          - table
486
     *                          - Columns
487
     * @return array couple of $before, $after
488
     */
489
    public function convertChangelogValues(Event $event, ArrayObject $data)
490
    {
491
        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...
492
        /**
493
         * @var \Cake\ORM\Entity $entity
494
         * @var bool $isForeignKey
495
         * @var bool $isAssociation
496
         * @var mixed $before
497
         * @var mixed $after
498
         * @var array $beforeValues
499
         * @var array $afterValues
500
         * @var string $column
501
         * @var array $columnDef
502
         * @var \Cake\ORM\Table $table
503
         * @var \Cake\ORM\Table $Columns
504
         */
505
506
        /**
507
         * Date inputs sometime represents string value in
508
         * entity. This converts value for comparison.
509
         */
510
        if ($this->config('convertDatetimeColumns') && !$isAssociation && isset($columnDef['type'])) {
511
            switch ($columnDef['type']) {
512
                case 'date':
513
                case 'datetime':
514
                case 'time':
515
                    $baseType = $table->schema()->baseColumnType($column);
516
                    if ($baseType && Type::map($baseType)) {
517
                        $driver = $table->connection()->driver();
518
                        $before = Type::build($baseType)->toPHP($before, $driver);
519
                        $after = Type::build($baseType)->toPHP($after, $driver);
520
                    }
521
                    break;
522
            }
523
        }
524
525
        /**
526
         * Converts foreign keys. This converts belongsTo ID columns to associated
527
         * entity. Then it takes display field for the table.
528
         */
529
        if ($isForeignKey && $this->config('convertForeignKeys')) {
530
            if ($this->config('exchangeForeignKey')) {
531
                unset($data['beforeValues'][$column]);
532
                unset($data['afterValues'][$column]);
533
                $column = $association->property();
534
                $data['column'] = $column;
535
            }
536
537
            $before = $association->findById($before)->first();
538
            // belongsTo association requires beforeValue to convert
539
            $this->_collectedBeforeValues[$association->property()] = $before ? $before->toArray() : [];
540
            $after = $association->findById($after)->first();
541
            $before = $this->convertAssociationChangeValue($before, $association, 'before');
542
            $after = $this->convertAssociationChangeValue($after, $association, 'after');
543
        }
544
545
        /**
546
         * Converts associations
547
         */
548
        $converter = $this->config('convertAssociations');
549
        if ($isAssociation && $converter) {
550
            /** 
551
             * If array was given, handles it as whitelist of associations
552
             */
553
            if (!is_array($converter) || is_callable($converter) || in_array($column, $converter)) {
554
                $before = $this->convertAssociationChangeValue($before, $association, 'before');
555
                $after = $this->convertAssociationChangeValue($after, $association, 'after');
556
            }
557
        }
558
559
        /**
560
         * Modifies event data
561
         */
562
        $data['before'] = $before;
563
        $data['after'] = $after;
564
        $data['beforeValues'][$column] = $before;
565
        $data['afterValues'][$column] = $after;
566
    }
567
568
    /**
569
     * Default converter for association values
570
     */
571
    public function convertAssociationChangeValue($value, $association, $kind)
572
    {
573
        if (!$value) {
574
            return is_array($value) ? null : $value;
575
        }
576
577
        $isMany = in_array($association->type(), [Association::MANY_TO_MANY, Association::ONE_TO_MANY]);
578
        $property = $association->property();
579
        $beforeValue = Hash::get($this->_collectedBeforeValues, $property);
580
581
        /**
582
         * Call actual converter. callable can be set with `convertAssociations`
583
         * option.
584
         */
585
        $converter = $this->config('convertAssociations');
586
        $callable = is_callable($converter) ? $converter : [$this, 'defaultConvertAssociation'];
587
        $arguments = [$property, $value, $kind, $association, $isMany, $beforeValue];
588
589
        return call_user_func_array($callable, $arguments);
590
    }
591
592
    /**
593
     * Default converter for association values.
594
     *
595
     * @param string $property association property name
596
     * @param mixed $value expects EntityInterface/EntityInterface[]
597
     * @param string $kind either 'before'/'after'
598
     * @param \Cake\ORM\Association $association association object for the value
599
     * @param boolean $isMany true => [hasMany, belongsToMany] false => [hasOne, belongsTo]
600
     * @param array $beforeValue association original values. indexed by association properties.
601
     * @return mixed converted value
602
     */
603
    public function defaultConvertAssociation($property, $value, $kind, $association, $isMany, $beforeValue)
604
    {
605
        $displayField = $association->displayField();
606
        if ($kind === 'before' && !$beforeValue) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $beforeValue 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...
607
            return null;
608
        }
609
610
        if (!$value) {
611
            return null;
612
        }
613
614
        // hasMany, belongsToMany
615
        if ($isMany) {
616
            $values = $kind === 'before' ? $beforeValue : (array)$value;
617
            return implode(', ', collection($values)->extract($displayField)
618
                ->filter()
619
                ->toArray());
620
        // hasOne, belongsTo
621
        } else {
622
            if ($kind === 'before') {
623
                if ($beforeValue instanceof EntityInterface) {
624
                    return $beforeValue->get($displayField);
625
                }
626
627
                return Hash::get($beforeValue, $displayField);
628
            } else {
629
                if (!$value instanceof EntityInterface) {
630
                    return $value;
631
                }
632
                return $value->get($displayField);
633
            }
634
        }
635
    }
636
637
    /**
638
     * Default filter
639
     *
640
     * @param \Cake\Event\Event $event The event for callback
641
     * @param ArrayObject $data The event data. contains:
642
     *                          - entity
643
     *                          - isForeignKey
644
     *                          - isAssociation
645
     *                          - before
646
     *                          - after
647
     *                          - beforeValues
648
     *                          - afterValues
649
     *                          - column
650
     *                          - columnDef
651
     *                          - table
652
     *                          - Columns
653
     * @return bool column is changed or not
654
     */
655
    public function filterChanges(Event $event, ArrayObject $data)
656
    {
657
        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...
658
        /**
659
         * @var \Cake\ORM\Entity $entity
660
         * @var bool $isForeignKey
661
         * @var bool $isAssociation
662
         * @var mixed $before
663
         * @var mixed $after
664
         * @var array $beforeValues
665
         * @var array $afterValues
666
         * @var string $column
667
         * @var array $columnDef
668
         * @var \Cake\ORM\Table $table
669
         * @var \Cake\ORM\Table $Columns
670
         */
671
672
        /**
673
         * Filter e.g. null != ''
674
         */
675
        if ($this->config('equalComparison')) {
676
            return $before != $after;
677
        }
678
679
        /**
680
         * Filter foreign keys
681
         */
682
        if ($this->config('filterForeignKeys')) {
683
            return !$isForeignKey;
684
        }
685
686
        return true;
687
    }
688
689
    /**
690
     * Default save process
691
     *
692
     * @param \Cake\Event\Event $event The event for callback
693
     * @param \Cake\ORM\Table $Changelogs The table for parent
694
     * @param ArrayObject $data save data
695
     * @param ArrayObject $options save options
696
     * @return bool column is changed or not
697
     */
698
    public function saveChangelogRecords(Event $event, Table $Changelogs, ArrayObject $data, ArrayObject $options)
699
    {
700
        /**
701
         * Save changes to database
702
         */
703
        if ($this->config('autoSave')) {
704
            $changelog = $Changelogs->newEntity((array)$data);
705
            return $Changelogs->save($changelog, (array)$options);
706
        }
707
    }
708
709
}
710