Completed
Push — master ( 429a4e...429a4e )
by Jan
07:47 queued 03:47
created

StructuralDBElement::getParentID()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
1
<?php
2
/**
3
 *
4
 * part-db version 0.1
5
 * Copyright (C) 2005 Christoph Lechner
6
 * http://www.cl-projects.de/
7
 *
8
 * part-db version 0.2+
9
 * Copyright (C) 2009 K. Jacobs and others (see authors.php)
10
 * http://code.google.com/p/part-db/
11
 *
12
 * Part-DB Version 0.4+
13
 * Copyright (C) 2016 - 2019 Jan Böhmer
14
 * https://github.com/jbtronics
15
 *
16
 * This program is free software; you can redistribute it and/or
17
 * modify it under the terms of the GNU General Public License
18
 * as published by the Free Software Foundation; either version 2
19
 * of the License, or (at your option) any later version.
20
 *
21
 * This program is distributed in the hope that it will be useful,
22
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
24
 * GNU General Public License for more details.
25
 *
26
 * You should have received a copy of the GNU General Public License
27
 * along with this program; if not, write to the Free Software
28
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
29
 *
30
 */
31
32
declare(strict_types=1);
33
/**
34
 * Part-DB Version 0.4+ "nextgen"
35
 * Copyright (C) 2016 - 2019 Jan Böhmer
36
 * https://github.com/jbtronics.
37
 *
38
 * This program is free software; you can redistribute it and/or
39
 * modify it under the terms of the GNU General Public License
40
 * as published by the Free Software Foundation; either version 2
41
 * of the License, or (at your option) any later version.
42
 *
43
 * This program is distributed in the hope that it will be useful,
44
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
45
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
46
 * GNU General Public License for more details.
47
 *
48
 * You should have received a copy of the GNU General Public License
49
 * along with this program; if not, write to the Free Software
50
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
51
 */
52
53
namespace App\Entity\Base;
54
55
use App\Entity\Attachments\AttachmentContainingDBElement;
56
use Doctrine\Common\Collections\ArrayCollection;
57
use Doctrine\ORM\Mapping as ORM;
58
use App\Validator\Constraints\NoneOfItsChildren;
59
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
60
use Symfony\Component\Serializer\Annotation\Groups;
61
62
/**
63
 * All elements with the fields "id", "name" and "parent_id" (at least).
64
 *
65
 * This class is for managing all database objects with a structural design.
66
 * All these sub-objects must have the table columns 'id', 'name' and 'parent_id' (at least)!
67
 * The root node has always the ID '0'.
68
 * It's allowed to have instances of root elements, but if you try to change
69
 * an attribute of a root element, you will get an exception!
70
 *
71
 * @ORM\MappedSuperclass(repositoryClass="App\Repository\StructuralDBElementRepository")
72
 *
73
 * @ORM\EntityListeners({"App\Security\EntityListeners\ElementPermissionListener", "App\EntityListeners\TreeCacheInvalidationListener"})
74
 *
75
 * @UniqueEntity(fields={"name", "parent"}, ignoreNull=false, message="structural.entity.unique_name")
76
 */
77
abstract class StructuralDBElement extends AttachmentContainingDBElement
78
{
79
    public const ID_ROOT_ELEMENT = 0;
80
81
    //This is a not standard character, so build a const, so a dev can easily use it
82
    public const PATH_DELIMITER_ARROW = ' → ';
83
84
    // We can not define the mapping here or we will get an exception. Unfortunatly we have to do the mapping in the
85
    // subclasses
86
    /**
87
     * @var StructuralDBElement[]
88
     * @Groups({"include_children"})
89
     */
90
    protected $children;
91
    /**
92
     * @var StructuralDBElement
93
     * @NoneOfItsChildren()
94
     * @Groups({"include_parents"})
95
     */
96
    protected $parent;
97
98
    /**
99
     * @var string The comment info for this element
100
     * @ORM\Column(type="text")
101
     * @Groups({"simple", "extended", "full"})
102
     */
103
    protected $comment = '';
104
105
    /**
106
     * @var bool If this property is set, this element can not be selected for part properties.
107
     * Useful if this element should be used only for grouping, sorting.
108
     * @ORM\Column(type="boolean")
109
     */
110
    protected $not_selectable = false;
111
112
    /**
113
     * @var int
114
     */
115
    protected $level = 0;
116
117
    /** @var string[] all names of all parent elements as a array of strings,
118
     *  the last array element is the name of the element itself
119
     *
120
     */
121
    private $full_path_strings;
122
123
124
125
    public function __construct()
126
    {
127
        parent::__construct();
128
        $this->children = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new Doctrine\Common\Collections\ArrayCollection() of type Doctrine\Common\Collections\ArrayCollection is incompatible with the declared type App\Entity\Base\StructuralDBElement[] of property $children.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
129
    }
130
131
    /******************************************************************************
132
     * StructuralDBElement constructor.
133
     *****************************************************************************/
134
135
    /**
136
     * Check if this element is a child of another element (recursive).
137
     *
138
     * @param StructuralDBElement $another_element the object to compare
139
     *     IMPORTANT: both objects to compare must be from the same class (for example two "Device" objects)!
140
     *
141
     * @return bool True, if this element is child of $another_element.
142
     *
143
     * @throws \InvalidArgumentException if there was an error
144
     */
145
    public function isChildOf(StructuralDBElement $another_element)
146
    {
147
        $class_name = \get_class($this);
148
149
        //Check if both elements compared, are from the same type:
150
        if ($class_name !== \get_class($another_element)) {
151
            throw new \InvalidArgumentException('isChildOf() only works for objects of the same type!');
152
        }
153
154
        if (null === $this->getParent()) { // this is the root node
155
            return false;
156
        }
157
158
        //If this' parents element, is $another_element, then we are finished
159
        return ($this->parent->getID() === $another_element->getID())
160
            || $this->parent->isChildOf($another_element); //Otherwise, check recursivley
161
    }
162
163
    /**
164
     * Checks if this element is an root element (has no parent)
165
     * @return bool True if the this element is an root element.
166
     */
167
    public function isRoot() : bool
168
    {
169
        return $this->parent === null;
170
    }
171
172
    /******************************************************************************
173
     *
174
     * Getters
175
     *
176
     ******************************************************************************/
177
178
    /**
179
     * Get the parent of this element.
180
     *
181
     * @return StructuralDBElement|null The parent element. Null if this element, does not have a parent.
182
     */
183
    public function getParent(): ?self
184
    {
185
        return $this->parent;
186
    }
187
188
    /**
189
     *  Get the comment of the element.
190
191
     * @return string the comment
192
     */
193
    public function getComment(): ?string
194
    {
195
        return $this->comment;
196
    }
197
198
    /**
199
     * Get the level.
200
     *
201
     * The level of the root node is -1.
202
     *
203
     * @return int the level of this element (zero means a most top element
204
     *             [a subelement of the root node])
205
     *
206
     */
207
    public function getLevel(): int
208
    {
209
        /**
210
         * Only check for nodes that have a parent. In the other cases zero is correct.
211
         */
212
        if (0 === $this->level && $this->parent !== null) {
213
            $element = $this->parent;
214
            while ($element !== null) {
215
                /** @var StructuralDBElement $element */
216
                $element = $element->parent;
217
                ++$this->level;
218
            }
219
        }
220
        return $this->level;
221
    }
222
223
    /**
224
     * Get the full path.
225
     *
226
     * @param string $delimeter the delimeter of the returned string
227
     *
228
     * @return string the full path (incl. the name of this element), delimeted by $delimeter
229
     *
230
     */
231
    public function getFullPath(string $delimeter = self::PATH_DELIMITER_ARROW): string
232
    {
233
        if (!\is_array($this->full_path_strings)) {
0 ignored issues
show
introduced by
The condition is_array($this->full_path_strings) is always true.
Loading history...
234
            $this->full_path_strings = array();
235
            $this->full_path_strings[] = $this->getName();
236
            $element = $this;
237
238
            $overflow = 20; //We only allow 20 levels depth
239
240
            while (null !== $element->parent && $overflow >= 0) {
241
                $element = $element->parent;
242
                $this->full_path_strings[] = $element->getName();
243
                //Decrement to prevent mem overflow.
244
                $overflow--;
245
            }
246
247
            $this->full_path_strings = array_reverse($this->full_path_strings);
248
        }
249
250
        return implode($delimeter, $this->full_path_strings);
251
    }
252
253
254
    /**
255
     * Gets the path to this element (including the element itself)
256
     * @return self[] An array with all (recursivily) parent elements (including this one),
257
     * ordered from the lowest levels (root node) first to the highest level (the element itself)
258
     */
259
    public function getPathArray(): array
260
    {
261
        $tmp = [];
262
        $tmp[] = $this;
263
264
        //We only allow 20 levels depth
265
        while (!end($tmp)->isRoot() && count($tmp) < 20) {
266
            $tmp[] = end($tmp)->parent;
267
        }
268
269
        return array_reverse($tmp);
270
    }
271
272
    /**
273
     * Get all subelements of this element.
274
     *
275
     * @param bool $recursive if true, the search is recursive
276
     *
277
     * @return static[] all subelements as an array of objects (sorted by their full path)
278
     */
279
    public function getSubelements(): iterable
280
    {
281
        return $this->children;
282
    }
283
284
    public function getChildren(): iterable
285
    {
286
        return $this->children;
287
    }
288
289
    /**
290
     * @return bool
291
     */
292
    public function isNotSelectable(): bool
293
    {
294
        return $this->not_selectable;
295
    }
296
297
    /******************************************************************************
298
     *
299
     * Setters
300
     *
301
     ******************************************************************************/
302
303
    /**
304
     * Sets the new parent object
305
     * @param self $new_parent The new parent object
306
     * @return StructuralDBElement
307
     */
308
    public function setParent(?self $new_parent) : self
309
    {
310
        /*
311
        if ($new_parent->isChildOf($this)) {
312
            throw new \InvalidArgumentException('You can not use one of the element childs as parent!');
313
        } */
314
315
        $this->parent = $new_parent;
316
317
        return $this;
318
    }
319
320
    /**
321
     *  Set the comment.
322
     * @param string $new_comment the new comment
323
     * @return StructuralDBElement
324
     */
325
    public function setComment(?string $new_comment): self
326
    {
327
        $this->comment = $new_comment;
328
329
        return $this;
330
    }
331
332
    public function setChildren(array $element) : self
333
    {
334
        $this->children = $element;
335
336
        return $this;
337
    }
338
339
    /**
340
     * @param bool $not_selectable
341
     * @return StructuralDBElement
342
     */
343
    public function setNotSelectable(bool $not_selectable): StructuralDBElement
344
    {
345
        $this->not_selectable = $not_selectable;
346
        return $this;
347
    }
348
349
    public function clearChildren() : self
350
    {
351
        $this->children = new ArrayCollection();
0 ignored issues
show
Documentation Bug introduced by
It seems like new Doctrine\Common\Collections\ArrayCollection() of type Doctrine\Common\Collections\ArrayCollection is incompatible with the declared type App\Entity\Base\StructuralDBElement[] of property $children.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
352
353
        return $this;
354
    }
355
}
356