From 3bdb2b294f678d5f18b25c401b24bfd9b4a5fbab Mon Sep 17 00:00:00 2001 From: Sander Muller Date: Thu, 29 Aug 2024 18:28:51 +0200 Subject: [PATCH] Fix/issue 233 (#256) * Add failing test * reduce fatal error to phpstan failure * WIP * wip * wip * Refactoring --------- Co-authored-by: Jonas Staudenmeir --- src/Eloquent/Builder.php | 17 ++++++ src/Eloquent/Traits/HasAdjacencyList.php | 38 ++++++------- .../Traits/HasRecursiveRelationshipScopes.php | 55 ++++++++++--------- tests/Tree/EloquentTest.php | 20 +++++++ tests/Tree/Models/User.php | 12 ++++ 5 files changed, 97 insertions(+), 45 deletions(-) diff --git a/src/Eloquent/Builder.php b/src/Eloquent/Builder.php index aad99e7..1b2e15e 100644 --- a/src/Eloquent/Builder.php +++ b/src/Eloquent/Builder.php @@ -13,4 +13,21 @@ class Builder extends Base { use BuildsAdjacencyListQueries; + + /** + * The base query builder instance. + * + * @var \Staudenmeir\LaravelCte\Query\Builder + */ + protected $query; + + /** + * Get the underlying query builder instance. + * + * @return \Staudenmeir\LaravelCte\Query\Builder + */ + public function getQuery() + { + return $this->query; + } } diff --git a/src/Eloquent/Traits/HasAdjacencyList.php b/src/Eloquent/Traits/HasAdjacencyList.php index d4254ae..aacef24 100644 --- a/src/Eloquent/Traits/HasAdjacencyList.php +++ b/src/Eloquent/Traits/HasAdjacencyList.php @@ -143,7 +143,7 @@ public function ancestors() /** * Get the model's ancestors and itself. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors */ public function ancestorsAndSelf() { @@ -164,7 +164,7 @@ public function ancestorsAndSelf() * @param string $foreignKey * @param string $localKey * @param bool $andSelf - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors */ protected function newAncestors(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf) { @@ -174,7 +174,7 @@ protected function newAncestors(Builder $query, Model $parent, $foreignKey, $loc /** * Get the model's bloodline. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Bloodline + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Bloodline */ public function bloodline() { @@ -193,7 +193,7 @@ public function bloodline() * @param \Illuminate\Database\Eloquent\Model $parent * @param string $foreignKey * @param string $localKey - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Bloodline + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Bloodline */ protected function newBloodline(Builder $query, Model $parent, $foreignKey, $localKey) { @@ -203,7 +203,7 @@ protected function newBloodline(Builder $query, Model $parent, $foreignKey, $loc /** * Get the model's children. * - * @return \Illuminate\Database\Eloquent\Relations\HasMany + * @return \Illuminate\Database\Eloquent\Relations\HasMany */ public function children() { @@ -213,7 +213,7 @@ public function children() /** * Get the model's children and itself. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants */ public function childrenAndSelf() { @@ -223,7 +223,7 @@ public function childrenAndSelf() /** * Get the model's descendants. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants */ public function descendants() { @@ -239,7 +239,7 @@ public function descendants() /** * Get the model's descendants and itself. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants */ public function descendantsAndSelf() { @@ -260,7 +260,7 @@ public function descendantsAndSelf() * @param string $foreignKey * @param string $localKey * @param bool $andSelf - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Descendants */ protected function newDescendants(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf) { @@ -270,7 +270,7 @@ protected function newDescendants(Builder $query, Model $parent, $foreignKey, $l /** * Get the model's parent. * - * @return \Illuminate\Database\Eloquent\Relations\BelongsTo + * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function parent() { @@ -280,7 +280,7 @@ public function parent() /** * Get the model's parent and itself. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors */ public function parentAndSelf() { @@ -290,7 +290,7 @@ public function parentAndSelf() /** * Get the model's root ancestor. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor */ public function rootAncestor() { @@ -309,7 +309,7 @@ public function rootAncestor() * @param \Illuminate\Database\Eloquent\Model $parent * @param string $foreignKey * @param string $localKey - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestor */ protected function newRootAncestor(Builder $query, Model $parent, $foreignKey, $localKey) { @@ -319,7 +319,7 @@ protected function newRootAncestor(Builder $query, Model $parent, $foreignKey, $ /** * Get the model's root ancestor or self. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestorOrSelf + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestorOrSelf */ public function rootAncestorOrSelf(): RootAncestorOrSelf { @@ -338,7 +338,7 @@ public function rootAncestorOrSelf(): RootAncestorOrSelf * @param \Illuminate\Database\Eloquent\Model $parent * @param string $foreignKey * @param string $localKey - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestorOrSelf + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\RootAncestorOrSelf */ protected function newRootAncestorOrSelf(Builder $query, Model $parent, string $foreignKey, string $localKey): RootAncestorOrSelf { @@ -348,7 +348,7 @@ protected function newRootAncestorOrSelf(Builder $query, Model $parent, string $ /** * Get the model's siblings. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings */ public function siblings() { @@ -364,7 +364,7 @@ public function siblings() /** * Get the model's siblings and itself. * - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings */ public function siblingsAndSelf() { @@ -385,7 +385,7 @@ public function siblingsAndSelf() * @param string $foreignKey * @param string $localKey * @param bool $andSelf - * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Siblings */ protected function newSiblings(Builder $query, Model $parent, $foreignKey, $localKey, $andSelf) { @@ -436,7 +436,7 @@ public function isIntegerAttribute($attribute) * Create a new Eloquent query builder for the model. * * @param \Illuminate\Database\Query\Builder $query - * @return \Illuminate\Database\Eloquent\Builder|static + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function newEloquentBuilder($query) { diff --git a/src/Eloquent/Traits/HasRecursiveRelationshipScopes.php b/src/Eloquent/Traits/HasRecursiveRelationshipScopes.php index 1357ab3..61fbac3 100644 --- a/src/Eloquent/Traits/HasRecursiveRelationshipScopes.php +++ b/src/Eloquent/Traits/HasRecursiveRelationshipScopes.php @@ -12,9 +12,9 @@ trait HasRecursiveRelationshipScopes /** * Add a recursive expression for the whole tree to the query. * - * @param \Illuminate\Database\Eloquent\Builder $query + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query * @param int|null $maxDepth - * @return \Illuminate\Database\Eloquent\Builder + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeTree(Builder $query, $maxDepth = null) { @@ -28,10 +28,10 @@ public function scopeTree(Builder $query, $maxDepth = null) /** * Add a recursive expression for a custom tree to the query. * - * @param \Illuminate\Database\Eloquent\Builder $query + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query * @param callable|\Illuminate\Database\Eloquent\Model $constraint * @param int|null $maxDepth - * @return \Illuminate\Database\Eloquent\Builder + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeTreeOf(Builder $query, callable|Model $constraint, $maxDepth = null) { @@ -45,8 +45,8 @@ public function scopeTreeOf(Builder $query, callable|Model $constraint, $maxDept /** * Limit the query to models with children. * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeHasChildren(Builder $query) { @@ -60,19 +60,20 @@ public function scopeHasChildren(Builder $query) /** * Limit the query to models without children. * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeDoesntHaveChildren(Builder $query) { + /** @var \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder|static $query */ return $query->isLeaf(); } /** * Limit the query to models with a parent. * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeHasParent(Builder $query) { @@ -82,8 +83,8 @@ public function scopeHasParent(Builder $query) /** * Limit the query to leaf models. * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeIsLeaf(Builder $query) { @@ -97,8 +98,8 @@ public function scopeIsLeaf(Builder $query) /** * Limit the query to root models. * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeIsRoot(Builder $query) { @@ -108,10 +109,10 @@ public function scopeIsRoot(Builder $query) /** * Limit the query by depth. * - * @param \Illuminate\Database\Eloquent\Builder $query + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query * @param mixed $operator * @param mixed|null $value - * @return \Illuminate\Database\Eloquent\Builder + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeWhereDepth(Builder $query, $operator, $value = null) { @@ -123,8 +124,8 @@ public function scopeWhereDepth(Builder $query, $operator, $value = null) /** * Order the query breadth-first. * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeBreadthFirst(Builder $query) { @@ -134,8 +135,8 @@ public function scopeBreadthFirst(Builder $query) /** * Order the query depth-first. * - * @param \Illuminate\Database\Eloquent\Builder $query - * @return \Illuminate\Database\Eloquent\Builder + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeDepthFirst(Builder $query) { @@ -147,13 +148,13 @@ public function scopeDepthFirst(Builder $query) /** * Add a recursive expression for the relationship to the query. * - * @param \Illuminate\Database\Eloquent\Builder $query + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query * @param string $direction * @param callable $constraint * @param int $initialDepth * @param string|null $from * @param int|null $maxDepth - * @return \Illuminate\Database\Eloquent\Builder + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ public function scopeWithRelationshipExpression(Builder $query, $direction, callable $constraint, $initialDepth, $from = null, $maxDepth = null) { @@ -170,7 +171,9 @@ public function scopeWithRelationshipExpression(Builder $query, $direction, call $query->getModel()->setTable($name); - return $query->withRecursiveExpression($name, $expression)->from($name); + $query->getQuery()->withRecursiveExpression($name, $expression->getQuery())->from($name); + + return $query; } /** @@ -180,7 +183,7 @@ public function scopeWithRelationshipExpression(Builder $query, $direction, call * @param callable $constraint * @param int $initialDepth * @param string $from - * @return \Illuminate\Database\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ protected function getInitialQuery(ExpressionGrammar $grammar, callable $constraint, $initialDepth, $from) { @@ -221,7 +224,7 @@ protected function getInitialQuery(ExpressionGrammar $grammar, callable $constra * @param string $direction * @param string $from * @param int|null $maxDepth - * @return \Illuminate\Database\Eloquent\Builder $query + * @return \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder */ protected function getRecursiveQuery(ExpressionGrammar $grammar, $direction, $from, $maxDepth = null) { @@ -287,7 +290,7 @@ protected function getRecursiveQuery(ExpressionGrammar $grammar, $direction, $fr /** * Add join and where clauses to the recursive query for a relationship expression. * - * @param \Illuminate\Database\Eloquent\Builder $query + * @param \Staudenmeir\LaravelAdjacencyList\Eloquent\Builder $query * @param string $direction * @param string $name * @param array $joinColumns diff --git a/tests/Tree/EloquentTest.php b/tests/Tree/EloquentTest.php index bc27743..6d6a041 100644 --- a/tests/Tree/EloquentTest.php +++ b/tests/Tree/EloquentTest.php @@ -292,4 +292,24 @@ public function testWithMaxDepthWithNegativeDepth() $this->assertEquals([2, 5], $users->pluck('id')->all()); } + + public function testLazyLoadingWithMultipleScopes() + { + /** @var User $user */ + $user = User::find(8); + + $ancestorsAndSelf = $user->ancestorAndSelfWithMultipleScopes()->get(); + + $this->assertEquals([2, 5, 8], $ancestorsAndSelf->pluck('id')->all()); + } + + public function testEagerLoadingWithMultipleScopes() + { + /** @var User $user */ + $user = User::find(8); + + $ancestorsAndSelf = $user->ancestorAndSelfWithMultipleScopes; + + $this->assertEquals([2, 5, 8], $ancestorsAndSelf->pluck('id')->all()); + } } diff --git a/tests/Tree/Models/User.php b/tests/Tree/Models/User.php index 41868cb..96a9f13 100644 --- a/tests/Tree/Models/User.php +++ b/tests/Tree/Models/User.php @@ -10,6 +10,7 @@ use Staudenmeir\EloquentHasManyDeep\HasRelationships; use Staudenmeir\EloquentHasManyDeep\HasTableAlias; use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships; +use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\Ancestors; use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\BelongsToManyOfDescendants; use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\HasManyOfDescendants; use Staudenmeir\LaravelAdjacencyList\Eloquent\Relations\MorphToManyOfDescendants; @@ -23,6 +24,7 @@ * @property-read int $depth * @property-read string $path * @property-read \Staudenmeir\LaravelAdjacencyList\Tests\Tree\Models\Post|null $ancestorPost + * @property-read \Staudenmeir\LaravelAdjacencyList\Eloquent\Collection $ancestorAndSelfWithMultipleScopes * @property-read \Staudenmeir\LaravelAdjacencyList\Tests\Tree\Models\Post|null $descendantPost */ class User extends Model @@ -95,6 +97,16 @@ public function ancestorAndSelfPosts(): HasManyDeep ); } + /** + * @return Ancestors + */ + public function ancestorAndSelfWithMultipleScopes(): Ancestors + { + return $this->ancestorsAndSelf() + ->hasParent() + ->breadthFirst(); + } + /** * @return HasManyDeep */