Skip to content

Commit

Permalink
progress
Browse files Browse the repository at this point in the history
  • Loading branch information
ildyria committed Jun 9, 2024
1 parent e28fe0d commit 381305a
Show file tree
Hide file tree
Showing 13 changed files with 429 additions and 252 deletions.
44 changes: 43 additions & 1 deletion phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,46 @@ parameters:
stubFiles:
ignoreErrors:
- identifier: missingType.generics
- '#Interface must be located in "Contract" or "Contracts" namespace#'
- '#Interface must be located in "Contract" or "Contracts" namespace#'
- '#Dynamic call to static method Kalnoy\\Nestedset\\QueryBuilder<.*>::select\(\).#'
- '#Dynamic call to static method Kalnoy\\Nestedset\\QueryBuilder<.*>::from\(\).#'
- '#Dynamic call to static method Kalnoy\\Nestedset\\QueryBuilder<.*>::whereRaw\(\).#'
- '#Dynamic call to static method Kalnoy\\Nestedset\\QueryBuilder<.*>::whereNested\(\).#'
# bunch of false positives from Eloquent
# ----------------------------------------------------------------------------------------
- '#Parameter .* \$models .* of method .*::initRelation\(\) should be contravariant with parameter \$models .* of method .*::initRelation\(\)#'
- '#Parameter .* \$models .* of method .*::addEagerConstraints\(\) should be contravariant with parameter \$models .* of method .*::addEagerConstraints\(\)#'
- '#Parameter .* \$models .* of method .*::match\(\) should be contravariant with parameter \$models .* of method .*::match\(\)#'

# - '#Method .*::ancestorsOf\(\) should return Illuminate\\Database\\Eloquent\\Collection<.*, .* Illuminate\\Database\\Eloquent\\Model> but returns Illuminate\\Database\\Eloquent\\Collection<int, Illuminate\\Database\\Eloquent\\Model>.#'
# - '#Method .*::ancestorsAndSelf\(\) should return Illuminate\\Database\\Eloquent\\Collection<.*, .* Illuminate\\Database\\Eloquent\\Model> but returns Illuminate\\Database\\Eloquent\\Collection<int, Illuminate\\Database\\Eloquent\\Model>.#'
# - '#Method .*::descendantsOf\(\) should return Illuminate\\Database\\Eloquent\\Collection<.*, .* Illuminate\\Database\\Eloquent\\Model> but returns Illuminate\\Database\\Eloquent\\Collection<int, Illuminate\\Database\\Eloquent\\Model>.#'
# - '#Method .*::descendantsAndSelf\(\) should return Illuminate\\Database\\Eloquent\\Collection<.*, .* Illuminate\\Database\\Eloquent\\Model> but returns Illuminate\\Database\\Eloquent\\Collection<int, Illuminate\\Database\\Eloquent\\Model>.#'
# - '#Parameter #1 $query of method Kalnoy\Nestedset\BaseRelation<Tmodel of
# Illuminate\Database\Eloquent\Model&Kalnoy\Nestedset\Node>::addEagerConstraint() expects
# Kalnoy\Nestedset\QueryBuilder, Illuminate\Database\Eloquent\Builder given.#'
# - '#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\(\)#'
# - '#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\(\)#'
# - '#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\(\)#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::from\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::limit\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::offset\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::take\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::truncate\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::insert\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::select\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::orderBy\(\)#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::where(Not)?(Null|In|Between|Exists|Column|Year|Month|Day)?\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::delete\(\)#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::without\(\)#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::with\(\)#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::count\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::update\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::inRandomOrder\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::groupBy\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::latest\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::first\(\).#'
# - '#Dynamic call to static method (Illuminate\\Database\\Query\\Builder|Illuminate\\Database\\Eloquent\\(Builder|Relations\\.*))(<.*>)?::skip\(\).#'
# - '#Call to an undefined method Illuminate\\Database\\Eloquent\\.*::with(Only)?\(\)#'
# - '#Call to an undefined method Illuminate\\Database\\Eloquent\\Builder|Illuminate\\Database\\Eloquent\\Relations\\Relation::whereNotNull\(\).#'
# - '#Call to protected method asDateTime\(\) of class Illuminate\\Database\\Eloquent\\Model.#'
8 changes: 4 additions & 4 deletions src/AncestorsRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Illuminate\Database\Eloquent\Model;

/**
* @template T
* @disregard P1037
*/
class AncestorsRelation extends BaseRelation
{
Expand All @@ -25,12 +25,12 @@ public function addConstraints()
}

/**
* @param Model $model
* @param $related
* @param Model&Node $model
* @param Node $related
*
* @return bool
*/
protected function matches(Model $model, $related)
protected function matches(Model $model, $related): bool
{
return $related->isAncestorOf($model);
}
Expand Down
34 changes: 22 additions & 12 deletions src/BaseRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Query\Builder;

/**
* @template Tmodelkey
* @template Tmodel of Model&Node<Tmodelkey,Model>
* @extends Relation<Tmodel>
* @property Tmodel $related
* @property Tmodel $parent
* @method Tmodel getParent()
*/
abstract class BaseRelation extends Relation
{
/**
Expand Down Expand Up @@ -43,12 +51,12 @@ public function __construct(QueryBuilder $builder, Model $model)
}

/**
* @param Model $model
* @param $related
* @param Model&Node $model
* @param Node $related
*
* @return bool
*/
abstract protected function matches(Model $model, $related);
abstract protected function matches(Model&Node $model, Node $related): bool;

/**
* @param QueryBuilder $query
Expand All @@ -71,14 +79,14 @@ abstract protected function relationExistenceCondition(string $hash, string $tab
/**
* @param EloquentBuilder $query
* @param EloquentBuilder $parent
* @param string[] $columns
* @param mixed $columns
*
* @return mixed
* @return EloquentBuilder
*/
public function getRelationExistenceQuery(EloquentBuilder $query, EloquentBuilder $parent,
$columns = ['*']
) {
$query = $this->getParent()->replicate()->newScopedQuery()->select($columns);
$query = $this->getParent()->replicate()->newScopedQuery()->select($columns);

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

Expand All @@ -100,10 +108,10 @@ public function getRelationExistenceQuery(EloquentBuilder $query, EloquentBuilde
/**
* Initialize the relation on a set of models.
*
* @param array $models
* @param array<int,Tmodel> $models
* @param string $relation
*
* @return array
* @return array<int,Tmodel>
*/
public function initRelation(array $models, $relation)
{
Expand Down Expand Up @@ -135,7 +143,7 @@ public function getResults()
/**
* Set the constraints for an eager load of the relation.
*
* @param array $models
* @param array<int,Tmodel> $models
*
* @return void
*/
Expand All @@ -160,11 +168,11 @@ public function addEagerConstraints(array $models)
/**
* Match the eagerly loaded results to their parents.
*
* @param array $models
* @param array<int,Tmodel> $models
* @param EloquentCollection $results
* @param string $relation
*
* @return array
* @return array<int,Tmodel>
*/
public function match(array $models, EloquentCollection $results, $relation)
{
Expand All @@ -178,15 +186,17 @@ public function match(array $models, EloquentCollection $results, $relation)
}

/**
* @param Model $model
* @param Tmodel $model
* @param EloquentCollection $results
*
* @return Collection
*/
protected function matchForModel(Model $model, EloquentCollection $results)
{
/** @var Collection<int,Tmodel> */
$result = $this->related->newCollection();

/** @var Tmodel $related */
foreach ($results as $related) {
if ($this->matches($model, $related)) {
$result->push($related);
Expand Down
47 changes: 32 additions & 15 deletions src/Collection.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@

namespace Kalnoy\Nestedset;

use Illuminate\Database\Eloquent\Collection as BaseCollection;
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
use Illuminate\Database\Eloquent\Model;

final class Collection extends BaseCollection
/**
* @template TKey of array-key
* @template TModel of Model&Node
*
* @extends EloquentCollection<TKey, TModel>
*/
final class Collection extends EloquentCollection
{
/**
* Fill `parent` and `children` relationships for every node in the collection.
Expand All @@ -20,22 +26,24 @@ public function linkNodes()
return $this;
}

$groupedNodes = $this->groupBy($this->first()->getParentIdName());
/** @var Node */
$first = $this->first();
$groupedNodes = $this->groupBy($first->getParentIdName());

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

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

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

$node->setRelation('children', BaseCollection::make($children));
$node->setRelation('children', EloquentCollection::make($children));
}

return $this;
Expand Down Expand Up @@ -77,7 +85,7 @@ public function toTree($root = false)
/**
* @param mixed $root
*
* @return int|string|null
* @return int|string
*/
protected function getRootNodeId($root = false)
{
Expand All @@ -101,6 +109,10 @@ protected function getRootNodeId($root = false)
}
}

if ($root === null || $root === false) {
throw new NestedSetException('root is null or false.');
}

return $root;
}

Expand All @@ -110,32 +122,37 @@ protected function getRootNodeId($root = false)
*
* @param bool $root
*
* @return static
* @return Collection
*/
public function toFlatTree($root = false)
public function toFlatTree($root = false): Collection
{
$result = new static();
$result = new Collection();

if ($this->isEmpty()) {
return $result;
}

$groupedNodes = $this->groupBy($this->first()->getParentIdName());
/** @var Node */
$first = $this->first();
/** @var Collection<int|string,TModel> */
$groupedNodes = $this->groupBy($first->getParentIdName());

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

/**
* Flatten a tree into a non recursive array.
*
* @param Collection $groupedNodes
* @param mixed $parentId
* @param Collection<int|string,TModel> $groupedNodes
* @param int|string $parentId
*
* @return $this
* @return Collection
*/
protected function flattenTree(self $groupedNodes, $parentId)
protected function flattenTree(Collection $groupedNodes, $parentId): Collection
{
foreach ($groupedNodes->get($parentId, []) as $node) {
/** @var array<int,TModel> */
$nodes = $groupedNodes->get($parentId, []);
foreach ($nodes as $node) {
$this->push($node);

$this->flattenTree($groupedNodes, $node->getKey());
Expand Down
11 changes: 7 additions & 4 deletions src/DescendantsRelation.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

use Illuminate\Database\Eloquent\Model;

/**
* @disregard P1037
*/
class DescendantsRelation extends BaseRelation
{
/**
Expand Down Expand Up @@ -31,12 +34,12 @@ protected function addEagerConstraint($query, $model)
}

/**
* @param Model $model
* @param $related
* @param Model&Node $model
* @param Node $related
*
* @return mixed
* @return bool
*/
protected function matches(Model $model, $related)
protected function matches(Model $model, $related): bool
{
return $related->isDescendantOf($model);
}
Expand Down
15 changes: 15 additions & 0 deletions src/NestedSetException.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

namespace Kalnoy\Nestedset;

use Throwable;

/**
* Exception thrown when something really went wrong.
*/
class NestedSetException extends \Exception {

public function __construct(string $message, ?Throwable $previous = null) {
parent::__construct($message, 0, $previous);
}
}
2 changes: 2 additions & 0 deletions src/NestedSetServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,12 @@ class NestedSetServiceProvider extends ServiceProvider
public function register()
{
Blueprint::macro('nestedSet', function () {
/** @disregard P1006 */
NestedSet::columns($this);
});

Blueprint::macro('dropNestedSet', function () {
/** @disregard P1006 */
NestedSet::dropColumns($this);
});
}
Expand Down
Loading

0 comments on commit 381305a

Please sign in to comment.