umbrellio /
laravel-ltree
| 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
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
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
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
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
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
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 |