CollectionElementDeleted   A
last analyzed

Complexity

Total Complexity 40

Size/Duplication

Total Lines 167
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 88
c 0
b 0
f 0
dl 0
loc 167
rs 9.2
wmc 40

10 Methods

Rating   Name   Duplication   Size   Complexity  
A getDeletedElementID() 0 3 1
A getUndoMode() 0 8 2
A setUndoneEvent() 0 13 3
D resolveAbstractClassToInstantiableClass() 0 65 26
A getUndoEventID() 0 3 1
A getDeletedElementClass() 0 12 2
A __construct() 0 11 2
A isUndoEvent() 0 3 1
A getOldName() 0 3 1
A getCollectionName() 0 3 1

How to fix   Complexity   

Complex Class

Complex classes like CollectionElementDeleted often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use CollectionElementDeleted, and based on these observations, apply Extract Interface, too.

1
<?php
2
/*
3
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
4
 *
5
 *  Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
6
 *
7
 *  This program is free software: you can redistribute it and/or modify
8
 *  it under the terms of the GNU Affero General Public License as published
9
 *  by the Free Software Foundation, either version 3 of the License, or
10
 *  (at your option) any later version.
11
 *
12
 *  This program is distributed in the hope that it will be useful,
13
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
 *  GNU Affero General Public License for more details.
16
 *
17
 *  You should have received a copy of the GNU Affero General Public License
18
 *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 */
20
21
declare(strict_types=1);
22
23
/**
24
 * This file is part of Part-DB (https://github.com/Part-DB/Part-DB-symfony).
25
 *
26
 * Copyright (C) 2019 - 2022 Jan Böhmer (https://github.com/jbtronics)
27
 *
28
 * This program is free software: you can redistribute it and/or modify
29
 * it under the terms of the GNU Affero General Public License as published
30
 * by the Free Software Foundation, either version 3 of the License, or
31
 * (at your option) any later version.
32
 *
33
 * This program is distributed in the hope that it will be useful,
34
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36
 * GNU Affero General Public License for more details.
37
 *
38
 * You should have received a copy of the GNU Affero General Public License
39
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
40
 */
41
42
namespace App\Entity\LogSystem;
43
44
use App\Entity\Attachments\Attachment;
45
use App\Entity\Attachments\AttachmentType;
46
use App\Entity\Attachments\AttachmentTypeAttachment;
47
use App\Entity\Attachments\CategoryAttachment;
48
use App\Entity\Attachments\CurrencyAttachment;
49
use App\Entity\Attachments\ProjectAttachment;
50
use App\Entity\Attachments\FootprintAttachment;
51
use App\Entity\Attachments\GroupAttachment;
52
use App\Entity\Attachments\ManufacturerAttachment;
53
use App\Entity\Attachments\MeasurementUnitAttachment;
54
use App\Entity\Attachments\PartAttachment;
55
use App\Entity\Attachments\StorelocationAttachment;
56
use App\Entity\Attachments\SupplierAttachment;
57
use App\Entity\Attachments\UserAttachment;
58
use App\Entity\Base\AbstractDBElement;
59
use App\Entity\Contracts\LogWithEventUndoInterface;
60
use App\Entity\Contracts\NamedElementInterface;
61
use App\Entity\ProjectSystem\Project;
62
use App\Entity\Parameters\AbstractParameter;
63
use App\Entity\Parameters\AttachmentTypeParameter;
64
use App\Entity\Parameters\CategoryParameter;
65
use App\Entity\Parameters\CurrencyParameter;
66
use App\Entity\Parameters\ProjectParameter;
67
use App\Entity\Parameters\FootprintParameter;
68
use App\Entity\Parameters\GroupParameter;
69
use App\Entity\Parameters\ManufacturerParameter;
70
use App\Entity\Parameters\MeasurementUnitParameter;
71
use App\Entity\Parameters\PartParameter;
72
use App\Entity\Parameters\StorelocationParameter;
73
use App\Entity\Parameters\SupplierParameter;
74
use App\Entity\Parts\Category;
75
use App\Entity\Parts\Footprint;
76
use App\Entity\Parts\Manufacturer;
77
use App\Entity\Parts\MeasurementUnit;
78
use App\Entity\Parts\Part;
79
use App\Entity\Parts\Storelocation;
80
use App\Entity\Parts\Supplier;
81
use App\Entity\PriceInformations\Currency;
82
use App\Entity\UserSystem\Group;
83
use App\Entity\UserSystem\User;
84
use App\Repository\Parts\ManufacturerRepository;
85
use Doctrine\ORM\Mapping as ORM;
86
use InvalidArgumentException;
87
88
/**
89
 * @ORM\Entity()
90
 * This log entry is created when an element is deleted, that is used in a collection of an other entity.
91
 * This is needed to signal time travel, that it has to undelete the deleted entity.
92
 */
93
class CollectionElementDeleted extends AbstractLogEntry implements LogWithEventUndoInterface
94
{
95
    protected string $typeString = 'collection_element_deleted';
96
    protected int $level = self::LEVEL_INFO;
97
98
    public function __construct(AbstractDBElement $changed_element, string $collection_name, AbstractDBElement $deletedElement)
99
    {
100
        parent::__construct();
101
102
        $this->level = self::LEVEL_INFO;
103
        $this->setTargetElement($changed_element);
104
        $this->extra['n'] = $collection_name;
105
        $this->extra['c'] = self::targetTypeClassToID(get_class($deletedElement));
106
        $this->extra['i'] = $deletedElement->getID();
107
        if ($deletedElement instanceof NamedElementInterface) {
108
            $this->extra['o'] = $deletedElement->getName();
109
        }
110
    }
111
112
    /**
113
     * Get the name of the collection (on target element) that was changed.
114
     */
115
    public function getCollectionName(): string
116
    {
117
        return $this->extra['n'];
118
    }
119
120
    /**
121
     * Gets the name of the element that was deleted.
122
     * Return null, if the element did not have a name.
123
     */
124
    public function getOldName(): ?string
125
    {
126
        return $this->extra['o'] ?? null;
127
    }
128
129
    /**
130
     * Returns the class of the deleted element.
131
     */
132
    public function getDeletedElementClass(): string
133
    {
134
        //The class name of our target element
135
        $tmp = self::targetTypeIdToClass($this->extra['c']);
136
137
        $reflection_class = new \ReflectionClass($tmp);
138
        //If the class is abstract, we have to map it to an instantiable class
139
        if ($reflection_class->isAbstract()) {
140
            return $this->resolveAbstractClassToInstantiableClass($tmp);
141
        }
142
143
        return $tmp;
144
    }
145
146
    /**
147
     * This functions maps an abstract class name derived from the extra c element to an instantiable class name (based on the target element of this log entry).
148
     * For example if the target element is a part and the extra c element is "App\Entity\Attachments\Attachment", this function will return "App\Entity\Attachments\PartAttachment".
149
     * @param  string  $abstract_class
150
     * @return string
151
     */
152
    private function resolveAbstractClassToInstantiableClass(string $abstract_class): string
153
    {
154
        if (is_a($abstract_class, AbstractParameter::class, true)) {
155
            switch ($this->getTargetClass()) {
156
                case AttachmentType::class:
157
                    return AttachmentTypeParameter::class;
158
                case Category::class:
159
                    return CategoryParameter::class;
160
                case Currency::class:
161
                    return CurrencyParameter::class;
162
                case Project::class:
163
                    return ProjectParameter::class;
164
                case Footprint::class:
165
                    return FootprintParameter::class;
166
                case Group::class:
167
                    return GroupParameter::class;
168
                case Manufacturer::class:
169
                    return ManufacturerParameter::class;
170
                case MeasurementUnit::class:
171
                    return MeasurementUnitParameter::class;
172
                case Part::class:
173
                    return PartParameter::class;
174
                case Storelocation::class:
175
                    return StorelocationParameter::class;
176
                case Supplier::class:
177
                    return SupplierParameter::class;
178
179
                default:
180
                    throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass());
181
            }
182
        }
183
184
        if (is_a($abstract_class, Attachment::class, true)) {
185
            switch ($this->getTargetClass()) {
186
                case AttachmentType::class:
187
                    return AttachmentTypeAttachment::class;
188
                case Category::class:
189
                    return CategoryAttachment::class;
190
                case Currency::class:
191
                    return CurrencyAttachment::class;
192
                case Project::class:
193
                    return ProjectAttachment::class;
194
                case Footprint::class:
195
                    return FootprintAttachment::class;
196
                case Group::class:
197
                    return GroupAttachment::class;
198
                case Manufacturer::class:
199
                    return ManufacturerAttachment::class;
200
                case MeasurementUnit::class:
201
                    return MeasurementUnitAttachment::class;
202
                case Part::class:
203
                    return PartAttachment::class;
204
                case Storelocation::class:
205
                    return StorelocationAttachment::class;
206
                case Supplier::class:
207
                    return SupplierAttachment::class;
208
                case User::class:
209
                    return UserAttachment::class;
210
211
                default:
212
                    throw new \RuntimeException('Unknown target class for parameter: '.$this->getTargetClass());
213
            }
214
        }
215
216
        throw new \RuntimeException('The class '.$abstract_class.' is abstract and no explicit resolving to an concrete type is defined!');
217
    }
218
219
    /**
220
     * Returns the ID of the deleted element.
221
     */
222
    public function getDeletedElementID(): int
223
    {
224
        return $this->extra['i'];
225
    }
226
227
    public function isUndoEvent(): bool
228
    {
229
        return isset($this->extra['u']);
230
    }
231
232
    public function getUndoEventID(): ?int
233
    {
234
        return $this->extra['u'] ?? null;
235
    }
236
237
    public function setUndoneEvent(AbstractLogEntry $event, string $mode = 'undo'): LogWithEventUndoInterface
238
    {
239
        $this->extra['u'] = $event->getID();
240
241
        if ('undo' === $mode) {
242
            $this->extra['um'] = 1;
243
        } elseif ('revert' === $mode) {
244
            $this->extra['um'] = 2;
245
        } else {
246
            throw new InvalidArgumentException('Passed invalid $mode!');
247
        }
248
249
        return $this;
250
    }
251
252
    public function getUndoMode(): string
253
    {
254
        $mode_int = $this->extra['um'] ?? 1;
255
        if (1 === $mode_int) {
256
            return 'undo';
257
        }
258
259
        return 'revert';
260
    }
261
}
262