Skip to content

Commit ef388b6

Browse files
authored
Feature: Has one of many support (#141)
* Added support for hasOne()->ofMany() * Improved tests; Added additional check:
1 parent f818758 commit ef388b6

File tree

4 files changed

+93
-1
lines changed

4 files changed

+93
-1
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ vendor
55
coverage
66
.phpunit.result.cache
77
.php_cs.cache
8+
.envrc

src/Mixins/RelationshipsExtraMethods.php

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,33 @@
1414
use Illuminate\Database\Eloquent\Relations\MorphToMany;
1515

1616
/**
17-
* @method getModel
17+
* @method \Illuminate\Database\Eloquent\Model getModel()
18+
* @method string getTable()
19+
* @method string getForeignPivotKeyName()
20+
* @method string getRelatedPivotKeyName()
21+
* @method bool isOneOfMany()
22+
* @method \Illuminate\Database\Eloquent\Builder|void getOneOfManySubQuery()
23+
* @method \Illuminate\Database\Eloquent\Builder getQuery()
24+
* @method \Illuminate\Database\Eloquent\Model getThroughParent()
25+
* @method string getForeignKeyName()
26+
* @method string getMorphType()
27+
* @method string getMorphClass()
28+
* @method string getFirstKeyName()
29+
* @method string getQualifiedLocalKeyName()
30+
* @method string getExistenceCompareKey()
1831
* @mixin \Illuminate\Database\Eloquent\Relations\Relation
32+
* @mixin \Illuminate\Database\Eloquent\Relations\HasOneOrMany
33+
* @mixin \Illuminate\Database\Eloquent\Relations\BelongsToMany
34+
* @property \Illuminate\Database\Eloquent\Builder $query
35+
* @property \Illuminate\Database\Eloquent\Model $parent
36+
* @property \Illuminate\Database\Eloquent\Model $throughParent
37+
* @property string $foreignKey
38+
* @property string $parentKey
39+
* @property string $ownerKey
40+
* @property string $localKey
41+
* @property string $secondKey
42+
* @property string $secondLocalKey
43+
* @property \Illuminate\Database\Eloquent\Model $farParent
1944
*/
2045
class RelationshipsExtraMethods
2146
{
@@ -226,6 +251,15 @@ protected function performJoinForEloquentPowerJoinsForHasMany()
226251
return function ($builder, $joinType, $callback = null, $alias = null, bool $disableExtraConditions = false) {
227252
$joinedTable = $alias ?: $this->query->getModel()->getTable();
228253
$parentTable = StaticCache::getTableOrAliasForModel($this->parent);
254+
$isOneOfMany = method_exists($this, 'isOneOfMany') ? $this->isOneOfMany() : false;
255+
256+
if ($isOneOfMany) {
257+
foreach ($this->getOneOfManySubQuery()->getQuery()->columns as $column) {
258+
$builder->addSelect($column);
259+
}
260+
261+
$builder->take(1);
262+
}
229263

230264
$builder->{$joinType}($this->query->getModel()->getTable(), function ($join) use ($callback, $joinedTable, $parentTable, $alias, $disableExtraConditions) {
231265
if ($alias) {

tests/JoinRelationshipTest.php

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,4 +727,45 @@ public function test_it_can_alias_belongs_to_many()
727727
$sql
728728
);
729729
}
730+
731+
public function test_has_one_of_many()
732+
{
733+
$post = factory(Post::class)->create();
734+
$bestComment = factory(Comment::class)->state('approved')->create(['body' => 'best comment', 'votes' => 2]);
735+
$lastComment = factory(Comment::class)->state('approved')->create(['body' => 'worst comment', 'votes' => 0]);
736+
737+
$bestCommentSql = Post::query()
738+
->select('posts.*', 'comments.body')
739+
->joinRelationship('bestComment')
740+
->toSql();
741+
742+
$bestCommentPost = Post::query()
743+
->select('posts.*', 'comments.body')
744+
->joinRelationship('bestComment')
745+
->first();
746+
747+
$this->assertStringContainsString(
748+
'max("comments"."votes") as "votes_aggregate"',
749+
$bestCommentSql
750+
);
751+
752+
$this->assertEquals($bestComment->body, $bestCommentPost->body);
753+
754+
$lastCommentSql = Post::query()
755+
->select('posts.*', 'comments.body')
756+
->joinRelationship('lastComment')
757+
->toSql();
758+
759+
$lastCommentPost = Post::query()
760+
->select('posts.*', 'comments.body')
761+
->joinRelationship('lastComment')
762+
->first();
763+
764+
$this->assertStringContainsString(
765+
'max("comments"."id") as "id_aggregate"',
766+
strtolower($lastCommentSql)
767+
);
768+
769+
$this->assertEquals($lastComment->body, $lastCommentPost->body);
770+
}
730771
}

tests/Models/Post.php

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
use Kirschbaum\PowerJoins\PowerJoins;
66
use Illuminate\Database\Eloquent\Model;
7+
use Illuminate\Database\Eloquent\Builder;
8+
use Illuminate\Database\Eloquent\Relations\HasOne;
79
use Illuminate\Database\Eloquent\Relations\HasMany;
810
use Illuminate\Database\Eloquent\Relations\BelongsTo;
911
use Illuminate\Database\Eloquent\Relations\MorphMany;
@@ -36,6 +38,20 @@ public function comments(): HasMany
3638
return $this->hasMany(Comment::class);
3739
}
3840

41+
public function lastComment(): HasOne
42+
{
43+
return $this
44+
->hasOne(Comment::class)
45+
->ofMany();
46+
}
47+
48+
public function bestComment(): HasOne
49+
{
50+
return $this
51+
->hasOne(Comment::class)
52+
->ofMany('votes', 'max');
53+
}
54+
3955
public function images(): MorphMany
4056
{
4157
return $this->morphMany(Image::class, 'imageable');

0 commit comments

Comments
 (0)