1 | <?php |
||||||
2 | |||||||
3 | declare(strict_types=1); |
||||||
4 | |||||||
5 | namespace Umbrellio\LTree\Helpers; |
||||||
6 | |||||||
7 | use Illuminate\Database\Eloquent\Collection; |
||||||
8 | use Illuminate\Database\Eloquent\Model; |
||||||
9 | use InvalidArgumentException; |
||||||
10 | use Umbrellio\Common\Contracts\AbstractPresenter; |
||||||
11 | use Umbrellio\LTree\Collections\LTreeCollection; |
||||||
12 | use Umbrellio\LTree\Interfaces\LTreeInterface; |
||||||
13 | use Umbrellio\LTree\Interfaces\ModelInterface; |
||||||
14 | |||||||
15 | /** |
||||||
16 | * @property LTreeInterface|ModelInterface|Model $model |
||||||
17 | */ |
||||||
18 | class LTreeNode extends AbstractPresenter |
||||||
19 | { |
||||||
20 | protected $parent; |
||||||
21 | protected $children; |
||||||
22 | |||||||
23 | 23 | public function __construct($model = null) |
|||||
24 | { |
||||||
25 | 23 | parent::__construct($model); |
|||||
26 | } |
||||||
27 | |||||||
28 | 14 | public function isRoot(): bool |
|||||
29 | { |
||||||
30 | 14 | return $this->model === null; |
|||||
31 | } |
||||||
32 | |||||||
33 | 1 | public function getParent(): ?self |
|||||
34 | { |
||||||
35 | 1 | return $this->parent; |
|||||
36 | } |
||||||
37 | |||||||
38 | 22 | public function setParent(?self $parent): void |
|||||
39 | { |
||||||
40 | 22 | $this->parent = $parent; |
|||||
41 | } |
||||||
42 | |||||||
43 | 22 | public function addChild(self $node): void |
|||||
44 | { |
||||||
45 | 22 | $this |
|||||
46 | 22 | ->getChildren() |
|||||
47 | 22 | ->add($node); |
|||||
48 | 22 | $node->setParent($this); |
|||||
49 | } |
||||||
50 | |||||||
51 | 22 | public function getChildren(): Collection |
|||||
52 | { |
||||||
53 | 22 | if (!$this->children) { |
|||||
54 | 22 | $this->children = new Collection(); |
|||||
55 | } |
||||||
56 | 22 | return $this->children; |
|||||
57 | } |
||||||
58 | |||||||
59 | 1 | public function countDescendants(): int |
|||||
60 | { |
||||||
61 | 1 | return $this |
|||||
62 | 1 | ->getChildren() |
|||||
63 | 1 | ->reduce( |
|||||
64 | 1 | static function (int $count, self $node) { |
|||||
65 | 1 | return $count + $node->countDescendants(); |
|||||
66 | 1 | }, |
|||||
67 | 1 | $this |
|||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
68 | 1 | ->getChildren() |
|||||
69 | 1 | ->count() |
|||||
70 | 1 | ); |
|||||
71 | } |
||||||
72 | |||||||
73 | 7 | public function findInTree(int $id): ?self |
|||||
74 | { |
||||||
75 | 7 | if (!$this->isRoot() && $this->model->getKey() === $id) { |
|||||
0 ignored issues
–
show
The method
getKey() does not exist on Illuminate\Database\Eloquent\Model . It seems like you code against a sub-type of Illuminate\Database\Eloquent\Model such as Umbrellio\LTree\tests\_data\Models\CategoryStub or Umbrellio\LTree\tests\_data\Models\ProductStub .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() The method
getKey() does not exist on Umbrellio\LTree\Interfaces\LTreeInterface . Since it exists in all sub-types, consider adding an abstract or default implementation to Umbrellio\LTree\Interfaces\LTreeInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
76 | 4 | return $this; |
|||||
77 | } |
||||||
78 | 7 | foreach ($this->getChildren() as $child) { |
|||||
79 | 7 | $result = $child->findInTree($id); |
|||||
80 | 7 | if ($result !== null) { |
|||||
81 | 4 | return $result; |
|||||
82 | } |
||||||
83 | } |
||||||
84 | 5 | return null; |
|||||
85 | } |
||||||
86 | |||||||
87 | 8 | public function each(callable $callback): void |
|||||
88 | { |
||||||
89 | 8 | if (!$this->isRoot()) { |
|||||
90 | 8 | $callback($this); |
|||||
91 | } |
||||||
92 | 8 | $this |
|||||
93 | 8 | ->getChildren() |
|||||
94 | 8 | ->each(static function (self $node) use ($callback) { |
|||||
95 | 8 | $node->each($callback); |
|||||
96 | 8 | }); |
|||||
97 | } |
||||||
98 | |||||||
99 | 8 | public function toCollection(): LTreeCollection |
|||||
100 | { |
||||||
101 | 8 | $collection = new LTreeCollection(); |
|||||
102 | 8 | $this->each(static function (self $item) use ($collection) { |
|||||
103 | 8 | $collection->add($item->model); |
|||||
104 | 8 | }); |
|||||
105 | 8 | return $collection; |
|||||
106 | } |
||||||
107 | |||||||
108 | 1 | public function pathAsString() |
|||||
109 | { |
||||||
110 | 1 | return $this->model ? $this->model->getLtreePath(LTreeInterface::AS_STRING) : null; |
|||||
0 ignored issues
–
show
The method
getLtreePath() does not exist on Illuminate\Database\Eloquent\Model . It seems like you code against a sub-type of Illuminate\Database\Eloquent\Model such as Umbrellio\LTree\tests\_data\Models\CategoryStub or Umbrellio\LTree\tests\_data\Models\ProductStub .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() The method
getLtreePath() does not exist on Umbrellio\LTree\Interfaces\ModelInterface . Since it exists in all sub-types, consider adding an abstract or default implementation to Umbrellio\LTree\Interfaces\ModelInterface .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
111 | } |
||||||
112 | |||||||
113 | 1 | public function toTreeArray(callable $callback) |
|||||
114 | { |
||||||
115 | 1 | return $this->fillTreeArray($this->getChildren(), $callback); |
|||||
116 | } |
||||||
117 | |||||||
118 | /** |
||||||
119 | * Usage sortTree(['name' =>'asc', 'category'=>'desc']) or callback with arguments ($a, $b) and return -1 | 0 | 1 |
||||||
120 | * |
||||||
121 | * @param array|callable $options |
||||||
122 | */ |
||||||
123 | 4 | public function sortTree($options) |
|||||
124 | { |
||||||
125 | 4 | $children = $this->getChildren(); |
|||||
126 | 4 | $callback = $options; |
|||||
127 | 4 | if (!is_callable($options)) { |
|||||
128 | 4 | $callback = $this->optionsToCallback($options); |
|||||
0 ignored issues
–
show
It seems like
$options can also be of type callable ; however, parameter $options of Umbrellio\LTree\Helpers\...de::optionsToCallback() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
129 | } |
||||||
130 | 4 | $children->each(static function ($child) use ($callback) { |
|||||
131 | /** @var LTreeNode $child */ |
||||||
132 | 4 | $child->sortTree($callback); |
|||||
133 | 4 | }); |
|||||
134 | 4 | $this->children = $children |
|||||
135 | 4 | ->sort($callback) |
|||||
136 | 4 | ->values(); |
|||||
137 | } |
||||||
138 | |||||||
139 | 1 | private function fillTreeArray(iterable $nodes, callable $callback) |
|||||
140 | { |
||||||
141 | 1 | $data = []; |
|||||
142 | 1 | foreach ($nodes as $node) { |
|||||
143 | 1 | $item = $callback($node); |
|||||
144 | 1 | $children = $this->fillTreeArray($node->getChildren(), $callback); |
|||||
145 | 1 | $item['children'] = $children; |
|||||
146 | 1 | $data[] = $item; |
|||||
147 | } |
||||||
148 | 1 | return $data; |
|||||
149 | } |
||||||
150 | |||||||
151 | 4 | private function optionsToCallback(array $options): callable |
|||||
152 | { |
||||||
153 | 4 | return function ($a, $b) use ($options) { |
|||||
154 | 4 | foreach ($options as $property => $sort) { |
|||||
155 | 3 | if (!in_array(strtolower($sort), ['asc', 'desc'], true)) { |
|||||
156 | 1 | throw new InvalidArgumentException("Order '${sort}'' must be asc or desc"); |
|||||
157 | } |
||||||
158 | 2 | $order = strtolower($sort) === 'desc' ? -1 : 1; |
|||||
159 | 2 | $result = $a->{$property} <=> $b->{$property}; |
|||||
160 | 2 | if ($result !== 0) { |
|||||
161 | 2 | return $result * $order; |
|||||
162 | } |
||||||
163 | } |
||||||
164 | 1 | return 0; |
|||||
165 | 4 | }; |
|||||
166 | } |
||||||
167 | } |
||||||
168 |