From 0f599fcce45f9e2aa6bb57c76309286609efb5d7 Mon Sep 17 00:00:00 2001 From: Jonas Staudenmeir Date: Tue, 20 Jun 2023 19:37:48 +0200 Subject: [PATCH] Support HasOneDeep relationships --- composer.json | 2 +- .../IsConcatenableAncestorsRelation.php | 13 ++++++++++--- .../IsConcatenableDescendantsRelation.php | 13 ++++++++++--- tests/Tree/Concatenation/AncestorsTest.php | 12 ++++++++++++ tests/Tree/Concatenation/DescendantsTest.php | 10 ++++++++++ tests/Tree/Models/User.php | 17 +++++++++++++++++ 6 files changed, 60 insertions(+), 7 deletions(-) diff --git a/composer.json b/composer.json index 66cafd3..c12f287 100644 --- a/composer.json +++ b/composer.json @@ -20,7 +20,7 @@ "barryvdh/laravel-ide-helper": "^2.12", "mockery/mockery": "^1.5", "nesbot/carbon": "^2.62.1", - "staudenmeir/eloquent-has-many-deep": "^1.17" + "staudenmeir/eloquent-has-many-deep": "^1.17.2" }, "suggest": { "barryvdh/laravel-ide-helper": "Provide type hints for attributes and relations." diff --git a/src/Eloquent/Relations/Traits/Concatenation/IsConcatenableAncestorsRelation.php b/src/Eloquent/Relations/Traits/Concatenation/IsConcatenableAncestorsRelation.php index 10bb35f..8c883db 100644 --- a/src/Eloquent/Relations/Traits/Concatenation/IsConcatenableAncestorsRelation.php +++ b/src/Eloquent/Relations/Traits/Concatenation/IsConcatenableAncestorsRelation.php @@ -30,10 +30,15 @@ public function addEagerConstraintsToDeepRelationship(Builder $query, array $mod * @param array $models * @param \Illuminate\Database\Eloquent\Collection $results * @param string $relation + * @param string $type * @return array */ - public function matchResultsForDeepRelationship(array $models, Collection $results, string $relation): array - { + public function matchResultsForDeepRelationship( + array $models, + Collection $results, + string $relation, + string $type = 'many' + ): array { $dictionary = $this->buildDictionaryForDeepRelationship($results); $attribute = $this->andSelf ? $this->localKey : $this->getForeignKeyName(); @@ -42,7 +47,9 @@ public function matchResultsForDeepRelationship(array $models, Collection $resul $key = $model->$attribute; if (isset($dictionary[$key])) { - $value = $this->related->newCollection($dictionary[$key]); + $value = $dictionary[$key]; + + $value = $type === 'one' ? reset($value) : $this->related->newCollection($value); $model->setRelation($relation, $value); } diff --git a/src/Eloquent/Relations/Traits/Concatenation/IsConcatenableDescendantsRelation.php b/src/Eloquent/Relations/Traits/Concatenation/IsConcatenableDescendantsRelation.php index f59332f..938d2b1 100644 --- a/src/Eloquent/Relations/Traits/Concatenation/IsConcatenableDescendantsRelation.php +++ b/src/Eloquent/Relations/Traits/Concatenation/IsConcatenableDescendantsRelation.php @@ -36,17 +36,24 @@ public function addEagerConstraintsToDeepRelationship(Builder $query, array $mod * @param array $models * @param \Illuminate\Database\Eloquent\Collection $results * @param string $relation + * @param string $type * @return array */ - public function matchResultsForDeepRelationship(array $models, Collection $results, string $relation): array - { + public function matchResultsForDeepRelationship( + array $models, + Collection $results, + string $relation, + string $type = 'many' + ): array { $dictionary = $this->buildDictionaryForDeepRelationship($results); foreach ($models as $model) { $key = $model->{$this->localKey}; if (isset($dictionary[$key])) { - $value = $this->related->newCollection($dictionary[$key]); + $value = $dictionary[$key]; + + $value = $type === 'one' ? reset($value) : $this->related->newCollection($value); $model->setRelation($relation, $value); } diff --git a/tests/Tree/Concatenation/AncestorsTest.php b/tests/Tree/Concatenation/AncestorsTest.php index 3cdb89b..bcd244c 100644 --- a/tests/Tree/Concatenation/AncestorsTest.php +++ b/tests/Tree/Concatenation/AncestorsTest.php @@ -52,6 +52,18 @@ public function testEagerLoadingAndSelf() $this->assertEquals([100, 110], $users[10]->ancestorAndSelfPosts->pluck('id')->all()); } + public function testEagerLoadingWithHasOneDeep() + { + $users = User::with([ + 'ancestorPost' => fn (HasManyDeep $query) => $query->orderBy('id'), + ])->get(); + + $this->assertNull($users[0]->ancestorPost); + $this->assertEquals(10, $users[1]->ancestorPost->id); + $this->assertEquals(10, $users[7]->ancestorPost->id); + $this->assertNull($users[10]->ancestorPost); + } + public function testLazyEagerLoading() { $users = User::all()->load([ diff --git a/tests/Tree/Concatenation/DescendantsTest.php b/tests/Tree/Concatenation/DescendantsTest.php index c7832d2..5c65370 100644 --- a/tests/Tree/Concatenation/DescendantsTest.php +++ b/tests/Tree/Concatenation/DescendantsTest.php @@ -49,6 +49,16 @@ public function testEagerLoadingAndSelf() $this->assertEquals([100, 110], $users[9]->descendantPostsAndSelf->pluck('id')->all()); } + public function testEagerLoadingWithHasOneDeep() + { + $users = User::with('descendantPost')->get(); + + $this->assertEquals(20, $users[0]->descendantPost->id); + $this->assertEquals(50, $users[1]->descendantPost->id); + $this->assertNull($users[8]->descendantPost); + $this->assertEquals(100, $users[9]->descendantPost->id); + } + public function testLazyEagerLoading() { $users = User::all()->load('descendantPosts'); diff --git a/tests/Tree/Models/User.php b/tests/Tree/Models/User.php index d091180..12365e6 100644 --- a/tests/Tree/Models/User.php +++ b/tests/Tree/Models/User.php @@ -5,6 +5,7 @@ use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Staudenmeir\EloquentHasManyDeep\HasManyDeep; +use Staudenmeir\EloquentHasManyDeep\HasOneDeep; use Staudenmeir\EloquentHasManyDeep\HasRelationships; use Staudenmeir\EloquentHasManyDeep\HasTableAlias; use Staudenmeir\LaravelAdjacencyList\Eloquent\HasRecursiveRelationships; @@ -38,6 +39,14 @@ public function getCustomPaths() ); } + public function ancestorPost(): HasOneDeep + { + return $this->hasOneDeepFromRelations( + $this->ancestors(), + (new static())->hasMany(Post::class) + ); + } + public function ancestorPosts(): HasManyDeep { return $this->hasManyDeepFromRelations( @@ -62,6 +71,14 @@ public function bloodlinePosts(): HasManyDeep ); } + public function descendantPost(): HasOneDeep + { + return $this->hasOneDeepFromRelations( + $this->descendants(), + (new static())->hasMany(Post::class) + ); + } + public function descendantPosts(): HasManyDeep { return $this->hasManyDeepFromRelations(