PlaceholdedBehavior::beforeSave()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
eloc 3
c 1
b 0
f 1
dl 0
loc 7
rs 10
cc 3
nc 2
nop 2
1
<?php
2
declare(strict_types=1);
3
4
/**
5
 * BEdita, API-first content management framework
6
 * Copyright 2022 Atlas Srl, Chialab Srl
7
 *
8
 * This file is part of BEdita: you can redistribute it and/or modify
9
 * it under the terms of the GNU Lesser General Public License as published
10
 * by the Free Software Foundation, either version 3 of the License, or
11
 * (at your option) any later version.
12
 *
13
 * See LICENSE.LGPL or <http://gnu.org/licenses/lgpl-3.0.html> for more details.
14
 */
15
16
namespace BEdita\Placeholders\Model\Behavior;
17
18
use BEdita\Core\Exception\LockedResourceException;
19
use Cake\Datasource\EntityInterface;
20
use Cake\Event\Event;
21
use Cake\ORM\Behavior;
22
use Cake\Utility\Hash;
23
24
/**
25
 * Placeholded behavior
26
 */
27
class PlaceholdedBehavior extends Behavior
28
{
29
    use GetAssociationTrait;
30
31
    /**
32
     * Default configurations. Available configurations include:
33
     *
34
     * - `relations`: names of the BEdita relations to check.
35
     *
36
     * @var array<string, mixed>
37
     */
38
    protected $_defaultConfig = [
39
        'relations' => ['placeholded'],
40
    ];
41
42
    /**
43
     * Lock entity from being soft-deleted if it is placeholded somewhere.
44
     *
45
     * @param \Cake\Event\Event $event Dispatched event.
46
     * @param \Cake\Datasource\EntityInterface $entity Entity being saved.
47
     * @return void
48
     */
49
    public function beforeSave(Event $event, EntityInterface $entity): void
50
    {
51
        if (!$entity->isDirty('deleted') || !$entity->get('deleted')) {
52
            return;
53
        }
54
55
        $this->ensureNotPlaceholded($entity);
56
    }
57
58
    /**
59
     * Ensure an entity does not appear as a placeholder.
60
     *
61
     * @param \Cake\Datasource\EntityInterface $entity Entity being checked.
62
     * @return void
63
     * @throws \BEdita\Core\Exception\LockedResourceException
64
     */
65
    protected function ensureNotPlaceholded(EntityInterface $entity): void
66
    {
67
        $Table = $this->table();
68
69
        $relations = $this->getConfig('relations', []);
70
        foreach ($relations as $relation) {
71
            $association = $this->getAssociation($relation);
72
            if ($association === null) {
73
                continue;
74
            }
75
76
            $refCount = $Table->find()
77
                ->select(['existing' => 1])
78
                ->where((array)array_combine(
79
                    array_map([$Table, 'aliasField'], (array)$Table->getPrimaryKey()),
80
                    $entity->extract((array)$Table->getPrimaryKey())
81
                ))
82
                ->innerJoinWith($association->getName())
83
                ->count();
84
            if ($refCount > 0) {
85
                throw new LockedResourceException(__d(
86
                    'placeholders',
87
                    'Cannot delete object {0} because it is still {1} in {2,plural,=1{one object} other{# objects}}',
88
                    (string)Hash::get($entity, 'id'),
89
                    $relation,
90
                    $refCount
91
                ));
92
            }
93
        }
94
    }
95
}
96