HasParent::getChildren()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
c 0
b 0
f 0
ccs 2
cts 2
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Application\Traits;
6
7
use Doctrine\Common\Collections\Collection;
8
use InvalidArgumentException;
9
10
/**
11
 * Trait for all objects with a parent.
12
 *
13
 * ORM mapping must be defined in using class.
14
 */
15
trait HasParent
16
{
17
    private ?self $parent = null;
18
19
    /**
20
     * @var Collection<int, self>
21
     */
22
    private Collection $children;
23
24
    /**
25
     * Get the parent containing this object.
26
     *
27
     * @return null|self
28
     */
29 7
    public function getParent(): ?HasParentInterface
30
    {
31 7
        return $this->parent;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->parent could return the type Application\Traits\HasParent which is incompatible with the type-hinted return Application\Traits\HasParentInterface|null. Consider adding an additional type-check to rule them out.
Loading history...
32
    }
33
34
    /**
35
     * Set the parent containing this object.
36
     */
37 3
    public function setParent(?self $parent): void
38
    {
39
        // Remove from previous parent
40 3
        if ($this->parent) {
41 1
            $this->parent->getChildren()->removeElement($this);
42
        }
43
44
        // Add to new parent
45 3
        if ($parent) {
46 3
            $this->assertNotCyclic($parent);
0 ignored issues
show
Bug introduced by
$parent of type Application\Traits\HasParent is incompatible with the type Application\Traits\HasParentInterface expected by parameter $parentCandidate of Application\Traits\HasParent::assertNotCyclic(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

46
            $this->assertNotCyclic(/** @scrutinizer ignore-type */ $parent);
Loading history...
47 2
            $parent->children->add($this);
0 ignored issues
show
Bug introduced by
Accessing children on the interface Application\Traits\HasParentInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
48
        }
49
50 2
        $this->parent = $parent;
51
    }
52
53
    /**
54
     * Get children.
55
     */
56 7
    public function getChildren(): Collection
57
    {
58 7
        return $this->children;
59
    }
60
61
    /**
62
     * Has children.
63
     */
64 5
    public function hasChildren(): bool
65
    {
66 5
        return $this->getChildren()->count() > 0;
67
    }
68
69 3
    private function assertNotCyclic(HasParentInterface $parentCandidate): void
70
    {
71 3
        if ($parentCandidate === $this) {
0 ignored issues
show
introduced by
The condition $parentCandidate === $this is always false.
Loading history...
72 1
            throw new InvalidArgumentException('An object cannot be his own parent');
73
        }
74
75 2
        $allChildren = $this->getAllChildren();
76
77 2
        while ($parentCandidate) {
78 2
            if (in_array($parentCandidate, $allChildren, true)) {
79 1
                throw new InvalidArgumentException('Parent object is invalid because it would create a cyclic hierarchy');
80
            }
81
82 2
            $parentCandidate = $parentCandidate->getParent();
83
        }
84
    }
85
86
    /**
87
     * Get recursively all children and grand-children.
88
     *
89
     * @return HasParentInterface[]
90
     */
91 2
    private function getAllChildren(): array
92
    {
93 2
        $allChildren = [];
94 2
        foreach ($this->getChildren() as $child) {
95 1
            $allChildren[] = $child;
96 1
            $allChildren = array_merge($allChildren, $child->getAllChildren());
97
        }
98
99 2
        return $allChildren;
100
    }
101
102 1
    public function getHierarchicName(): string
103
    {
104 1
        $object = $this;
105 1
        $result = [];
106
107 1
        while ($object) {
108 1
            $result[] = $object->getName();
0 ignored issues
show
Bug introduced by
It seems like getName() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

108
            /** @scrutinizer ignore-call */ 
109
            $result[] = $object->getName();
Loading history...
109 1
            $object = $object->getParent();
110
        }
111
112 1
        return implode(' > ', array_reverse($result));
113
    }
114
115
    /**
116
     * @return self[]
117
     */
118 5
    public function getParentHierarchy(): array
119
    {
120 5
        $list = [];
121 5
        $parent = $this->getParent();
122
123 5
        if ($parent) {
124 2
            return array_merge($parent->getParentHierarchy(), [$parent]);
125
        }
126
127 5
        return $list;
128
    }
129
}
130