Skip to content

Commit 381305a

Browse files
committed
progress
1 parent e28fe0d commit 381305a

13 files changed

+429
-252
lines changed

phpstan.neon

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,46 @@ parameters:
99
stubFiles:
1010
ignoreErrors:
1111
- identifier: missingType.generics
12-
- '#Interface must be located in "Contract" or "Contracts" namespace#'
12+
- '#Interface must be located in "Contract" or "Contracts" namespace#'
13+
- '#Dynamic call to static method Kalnoy\\Nestedset\\QueryBuilder<.*>::select\(\).#'
14+
- '#Dynamic call to static method Kalnoy\\Nestedset\\QueryBuilder<.*>::from\(\).#'
15+
- '#Dynamic call to static method Kalnoy\\Nestedset\\QueryBuilder<.*>::whereRaw\(\).#'
16+
- '#Dynamic call to static method Kalnoy\\Nestedset\\QueryBuilder<.*>::whereNested\(\).#'
17+
# bunch of false positives from Eloquent
18+
# ----------------------------------------------------------------------------------------
19+
- '#Parameter .* \$models .* of method .*::initRelation\(\) should be contravariant with parameter \$models .* of method .*::initRelation\(\)#'
20+
- '#Parameter .* \$models .* of method .*::addEagerConstraints\(\) should be contravariant with parameter \$models .* of method .*::addEagerConstraints\(\)#'
21+
- '#Parameter .* \$models .* of method .*::match\(\) should be contravariant with parameter \$models .* of method .*::match\(\)#'
22+
23+
# - '#Method .*::ancestorsOf\(\) should return Illuminate\\Database\\Eloquent\\Collection<.*, .* Illuminate\\Database\\Eloquent\\Model> but returns Illuminate\\Database\\Eloquent\\Collection<int, Illuminate\\Database\\Eloquent\\Model>.#'
24+
# - '#Method .*::ancestorsAndSelf\(\) should return Illuminate\\Database\\Eloquent\\Collection<.*, .* Illuminate\\Database\\Eloquent\\Model> but returns Illuminate\\Database\\Eloquent\\Collection<int, Illuminate\\Database\\Eloquent\\Model>.#'
25+
# - '#Method .*::descendantsOf\(\) should return Illuminate\\Database\\Eloquent\\Collection<.*, .* Illuminate\\Database\\Eloquent\\Model> but returns Illuminate\\Database\\Eloquent\\Collection<int, Illuminate\\Database\\Eloquent\\Model>.#'
26+
# - '#Method .*::descendantsAndSelf\(\) should return Illuminate\\Database\\Eloquent\\Collection<.*, .* Illuminate\\Database\\Eloquent\\Model> but returns Illuminate\\Database\\Eloquent\\Collection<int, Illuminate\\Database\\Eloquent\\Model>.#'
27+
# - '#Parameter #1 $query of method Kalnoy\Nestedset\BaseRelation<Tmodel of
28+
# Illuminate\Database\Eloquent\Model&Kalnoy\Nestedset\Node>::addEagerConstraint() expects
29+
# Kalnoy\Nestedset\QueryBuilder, Illuminate\Database\Eloquent\Builder given.#'
30+
# - '#Parameter \#1 \$model \(Illuminate\\Database\\Eloquent\\Model&Kalnoy\\Nestedset\\Node\) of method Kalnoy\\Nestedset\\AncestorsRelation::matches\(\) should be contravariant with parameter \$model \(Illuminate\\Database\\Eloquent\\Model\) of method Kalnoy\\Nestedset\\BaseRelation::matches\(\)#'
31+
# - '#Parameter \#2 \$related \(Kalnoy\\Nestedset\\Node\) of method Kalnoy\\Nestedset\\AncestorsRelation::matches\(\) should be contravariant with parameter \$related \(mixed\) of method Kalnoy\\Nestedset\\BaseRelation::matches\(\)#'
32+
# - '#Parameter \#1 \$models \(array<int, Tmodel>\) of method Kalnoy\\Nestedset\\BaseRelation::initRelation\(\) should be contravariant with parameter \$models \(array\) of method Illuminate\\Database\\Eloquent\\Relations\\Relation<Illuminate\\Database\\Eloquent\\Model>::initRelation\(\)#'
33+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::from\(\).#'
34+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::limit\(\).#'
35+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::offset\(\).#'
36+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::take\(\).#'
37+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::truncate\(\).#'
38+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::insert\(\).#'
39+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::select\(\).#'
40+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::orderBy\(\)#'
41+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::where(Not)?(Null|In|Between|Exists|Column|Year|Month|Day)?\(\).#'
42+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::delete\(\)#'
43+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::without\(\)#'
44+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::with\(\)#'
45+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::count\(\).#'
46+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::update\(\).#'
47+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::inRandomOrder\(\).#'
48+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::groupBy\(\).#'
49+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::latest\(\).#'
50+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::first\(\).#'
51+
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::skip\(\).#'
52+
# - '#Call to an undefined method Illuminate\\Database\\Eloquent\\.*::with(Only)?\(\)#'
53+
# - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder|Illuminate\\Database\\Eloquent\\Relations\\Relation::whereNotNull\(\).#'
54+
# - '#Call to protected method asDateTime\(\) of class Illuminate\\Database\\Eloquent\\Model.#'

src/AncestorsRelation.php

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use Illuminate\Database\Eloquent\Model;
66

77
/**
8-
* @template T
8+
* @disregard P1037
99
*/
1010
class AncestorsRelation extends BaseRelation
1111
{
@@ -25,12 +25,12 @@ public function addConstraints()
2525
}
2626

2727
/**
28-
* @param Model $model
29-
* @param $related
28+
* @param Model&Node $model
29+
* @param Node $related
3030
*
3131
* @return bool
3232
*/
33-
protected function matches(Model $model, $related)
33+
protected function matches(Model $model, $related): bool
3434
{
3535
return $related->isAncestorOf($model);
3636
}

src/BaseRelation.php

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@
88
use Illuminate\Database\Eloquent\Relations\Relation;
99
use Illuminate\Database\Query\Builder;
1010

11+
/**
12+
* @template Tmodelkey
13+
* @template Tmodel of Model&Node<Tmodelkey,Model>
14+
* @extends Relation<Tmodel>
15+
* @property Tmodel $related
16+
* @property Tmodel $parent
17+
* @method Tmodel getParent()
18+
*/
1119
abstract class BaseRelation extends Relation
1220
{
1321
/**
@@ -43,12 +51,12 @@ public function __construct(QueryBuilder $builder, Model $model)
4351
}
4452

4553
/**
46-
* @param Model $model
47-
* @param $related
54+
* @param Model&Node $model
55+
* @param Node $related
4856
*
4957
* @return bool
5058
*/
51-
abstract protected function matches(Model $model, $related);
59+
abstract protected function matches(Model&Node $model, Node $related): bool;
5260

5361
/**
5462
* @param QueryBuilder $query
@@ -71,14 +79,14 @@ abstract protected function relationExistenceCondition(string $hash, string $tab
7179
/**
7280
* @param EloquentBuilder $query
7381
* @param EloquentBuilder $parent
74-
* @param string[] $columns
82+
* @param mixed $columns
7583
*
76-
* @return mixed
84+
* @return EloquentBuilder
7785
*/
7886
public function getRelationExistenceQuery(EloquentBuilder $query, EloquentBuilder $parent,
7987
$columns = ['*']
8088
) {
81-
$query = $this->getParent()->replicate()->newScopedQuery()->select($columns);
89+
$query = $this->getParent()->replicate()->newScopedQuery()->select($columns);
8290

8391
$table = $query->getModel()->getTable();
8492

@@ -100,10 +108,10 @@ public function getRelationExistenceQuery(EloquentBuilder $query, EloquentBuilde
100108
/**
101109
* Initialize the relation on a set of models.
102110
*
103-
* @param array $models
111+
* @param array<int,Tmodel> $models
104112
* @param string $relation
105113
*
106-
* @return array
114+
* @return array<int,Tmodel>
107115
*/
108116
public function initRelation(array $models, $relation)
109117
{
@@ -135,7 +143,7 @@ public function getResults()
135143
/**
136144
* Set the constraints for an eager load of the relation.
137145
*
138-
* @param array $models
146+
* @param array<int,Tmodel> $models
139147
*
140148
* @return void
141149
*/
@@ -160,11 +168,11 @@ public function addEagerConstraints(array $models)
160168
/**
161169
* Match the eagerly loaded results to their parents.
162170
*
163-
* @param array $models
171+
* @param array<int,Tmodel> $models
164172
* @param EloquentCollection $results
165173
* @param string $relation
166174
*
167-
* @return array
175+
* @return array<int,Tmodel>
168176
*/
169177
public function match(array $models, EloquentCollection $results, $relation)
170178
{
@@ -178,15 +186,17 @@ public function match(array $models, EloquentCollection $results, $relation)
178186
}
179187

180188
/**
181-
* @param Model $model
189+
* @param Tmodel $model
182190
* @param EloquentCollection $results
183191
*
184192
* @return Collection
185193
*/
186194
protected function matchForModel(Model $model, EloquentCollection $results)
187195
{
196+
/** @var Collection<int,Tmodel> */
188197
$result = $this->related->newCollection();
189198

199+
/** @var Tmodel $related */
190200
foreach ($results as $related) {
191201
if ($this->matches($model, $related)) {
192202
$result->push($related);

src/Collection.php

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,16 @@
22

33
namespace Kalnoy\Nestedset;
44

5-
use Illuminate\Database\Eloquent\Collection as BaseCollection;
5+
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
66
use Illuminate\Database\Eloquent\Model;
77

8-
final class Collection extends BaseCollection
8+
/**
9+
* @template TKey of array-key
10+
* @template TModel of Model&Node
11+
*
12+
* @extends EloquentCollection<TKey, TModel>
13+
*/
14+
final class Collection extends EloquentCollection
915
{
1016
/**
1117
* Fill `parent` and `children` relationships for every node in the collection.
@@ -20,22 +26,24 @@ public function linkNodes()
2026
return $this;
2127
}
2228

23-
$groupedNodes = $this->groupBy($this->first()->getParentIdName());
29+
/** @var Node */
30+
$first = $this->first();
31+
$groupedNodes = $this->groupBy($first->getParentIdName());
2432

2533
/** @var Node&Model $node */
2634
foreach ($this->items as $node) {
2735
if ($node->getParentId() === null) {
2836
$node->setRelation('parent', null);
2937
}
3038

39+
/** @var array<int,Model&Node> */
3140
$children = $groupedNodes->get($node->getKey(), []);
3241

33-
/** @var Model&Node $child */
3442
foreach ($children as $child) {
3543
$child->setRelation('parent', $node);
3644
}
3745

38-
$node->setRelation('children', BaseCollection::make($children));
46+
$node->setRelation('children', EloquentCollection::make($children));
3947
}
4048

4149
return $this;
@@ -77,7 +85,7 @@ public function toTree($root = false)
7785
/**
7886
* @param mixed $root
7987
*
80-
* @return int|string|null
88+
* @return int|string
8189
*/
8290
protected function getRootNodeId($root = false)
8391
{
@@ -101,6 +109,10 @@ protected function getRootNodeId($root = false)
101109
}
102110
}
103111

112+
if ($root === null || $root === false) {
113+
throw new NestedSetException('root is null or false.');
114+
}
115+
104116
return $root;
105117
}
106118

@@ -110,32 +122,37 @@ protected function getRootNodeId($root = false)
110122
*
111123
* @param bool $root
112124
*
113-
* @return static
125+
* @return Collection
114126
*/
115-
public function toFlatTree($root = false)
127+
public function toFlatTree($root = false): Collection
116128
{
117-
$result = new static();
129+
$result = new Collection();
118130

119131
if ($this->isEmpty()) {
120132
return $result;
121133
}
122134

123-
$groupedNodes = $this->groupBy($this->first()->getParentIdName());
135+
/** @var Node */
136+
$first = $this->first();
137+
/** @var Collection<int|string,TModel> */
138+
$groupedNodes = $this->groupBy($first->getParentIdName());
124139

125140
return $result->flattenTree($groupedNodes, $this->getRootNodeId($root));
126141
}
127142

128143
/**
129144
* Flatten a tree into a non recursive array.
130145
*
131-
* @param Collection $groupedNodes
132-
* @param mixed $parentId
146+
* @param Collection<int|string,TModel> $groupedNodes
147+
* @param int|string $parentId
133148
*
134-
* @return $this
149+
* @return Collection
135150
*/
136-
protected function flattenTree(self $groupedNodes, $parentId)
151+
protected function flattenTree(Collection $groupedNodes, $parentId): Collection
137152
{
138-
foreach ($groupedNodes->get($parentId, []) as $node) {
153+
/** @var array<int,TModel> */
154+
$nodes = $groupedNodes->get($parentId, []);
155+
foreach ($nodes as $node) {
139156
$this->push($node);
140157

141158
$this->flattenTree($groupedNodes, $node->getKey());

src/DescendantsRelation.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
use Illuminate\Database\Eloquent\Model;
66

7+
/**
8+
* @disregard P1037
9+
*/
710
class DescendantsRelation extends BaseRelation
811
{
912
/**
@@ -31,12 +34,12 @@ protected function addEagerConstraint($query, $model)
3134
}
3235

3336
/**
34-
* @param Model $model
35-
* @param $related
37+
* @param Model&Node $model
38+
* @param Node $related
3639
*
37-
* @return mixed
40+
* @return bool
3841
*/
39-
protected function matches(Model $model, $related)
42+
protected function matches(Model $model, $related): bool
4043
{
4144
return $related->isDescendantOf($model);
4245
}

src/NestedSetException.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace Kalnoy\Nestedset;
4+
5+
use Throwable;
6+
7+
/**
8+
* Exception thrown when something really went wrong.
9+
*/
10+
class NestedSetException extends \Exception {
11+
12+
public function __construct(string $message, ?Throwable $previous = null) {
13+
parent::__construct($message, 0, $previous);
14+
}
15+
}

src/NestedSetServiceProvider.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,12 @@ class NestedSetServiceProvider extends ServiceProvider
1010
public function register()
1111
{
1212
Blueprint::macro('nestedSet', function () {
13+
/** @disregard P1006 */
1314
NestedSet::columns($this);
1415
});
1516

1617
Blueprint::macro('dropNestedSet', function () {
18+
/** @disregard P1006 */
1719
NestedSet::dropColumns($this);
1820
});
1921
}

0 commit comments

Comments
 (0)