Issues (56)

src/Helpers/LTreeNode.php (6 issues)

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
$this->getChildren()->count() of type integer is incompatible with the type Illuminate\Support\Traits\TReduceInitial expected by parameter $initial of Illuminate\Support\Collection::reduce(). ( Ignorable by Annotation )

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

67
                /** @scrutinizer ignore-type */ $this
Loading history...
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 ignore-call  annotation

75
        if (!$this->isRoot() && $this->model->/** @scrutinizer ignore-call */ getKey() === $id) {
Loading history...
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 ignore-call  annotation

75
        if (!$this->isRoot() && $this->model->/** @scrutinizer ignore-call */ getKey() === $id) {
Loading history...
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 ignore-call  annotation

110
        return $this->model ? $this->model->/** @scrutinizer ignore-call */ getLtreePath(LTreeInterface::AS_STRING) : null;
Loading history...
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 ignore-call  annotation

110
        return $this->model ? $this->model->/** @scrutinizer ignore-call */ getLtreePath(LTreeInterface::AS_STRING) : null;
Loading history...
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 ignore-type  annotation

128
            $callback = $this->optionsToCallback(/** @scrutinizer ignore-type */ $options);
Loading history...
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