Skip to content

Commit

Permalink
[11.x] Fixes through() relationship (#52318)
Browse files Browse the repository at this point in the history
* Adds a failing test

* Allows MorphOneOrMany in through() method
  • Loading branch information
leobeal authored Jul 30, 2024
1 parent 9c57afc commit 702cb88
Show file tree
Hide file tree
Showing 2 changed files with 140 additions and 10 deletions.
28 changes: 18 additions & 10 deletions src/Illuminate/Database/Eloquent/PendingHasThroughRelationship.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use BadMethodCallException;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOneOrMany;
use Illuminate\Support\Str;

/**
Expand Down Expand Up @@ -44,7 +46,7 @@ public function __construct($rootModel, $localRelationship)
*
* @template TRelatedModel of \Illuminate\Database\Eloquent\Model
*
* @param string|(callable(TIntermediateModel): (\Illuminate\Database\Eloquent\Relations\HasOne<TRelatedModel, TIntermediateModel>|\Illuminate\Database\Eloquent\Relations\HasMany<TRelatedModel, TIntermediateModel>)) $callback
* @param string|(callable(TIntermediateModel): (\Illuminate\Database\Eloquent\Relations\HasOne<TRelatedModel, TIntermediateModel>|\Illuminate\Database\Eloquent\Relations\HasMany<TRelatedModel, TIntermediateModel>|\Illuminate\Database\Eloquent\Relations\MorphOneOrMany<TRelatedModel, TIntermediateModel>)) $callback
* @return (
* $callback is string
* ? \Illuminate\Database\Eloquent\Relations\HasManyThrough<\Illuminate\Database\Eloquent\Model, TIntermediateModel, TDeclaringModel>|\Illuminate\Database\Eloquent\Relations\HasOneThrough<\Illuminate\Database\Eloquent\Model, TIntermediateModel, TDeclaringModel>
Expand All @@ -64,24 +66,30 @@ public function has($callback)
$distantRelation = $callback($this->localRelationship->getRelated());

if ($distantRelation instanceof HasMany) {
return $this->rootModel->hasManyThrough(
$returnedRelation = $this->rootModel->hasManyThrough(
$distantRelation->getRelated()::class,
$this->localRelationship->getRelated()::class,
$this->localRelationship->getForeignKeyName(),
$distantRelation->getForeignKeyName(),
$this->localRelationship->getLocalKeyName(),
$distantRelation->getLocalKeyName(),
);
} else {
$returnedRelation = $this->rootModel->hasOneThrough(
$distantRelation->getRelated()::class,
$this->localRelationship->getRelated()::class,
$this->localRelationship->getForeignKeyName(),
$distantRelation->getForeignKeyName(),
$this->localRelationship->getLocalKeyName(),
$distantRelation->getLocalKeyName(),
);
}

if ($this->localRelationship instanceof MorphOneOrMany) {
$returnedRelation->where($this->localRelationship->getQualifiedMorphType(), $this->localRelationship->getMorphClass());
}

return $this->rootModel->hasOneThrough(
$distantRelation->getRelated()::class,
$this->localRelationship->getRelated()::class,
$this->localRelationship->getForeignKeyName(),
$distantRelation->getForeignKeyName(),
$this->localRelationship->getLocalKeyName(),
$distantRelation->getLocalKeyName(),
);
return $returnedRelation;
}

/**
Expand Down
122 changes: 122 additions & 0 deletions tests/Integration/Database/EloquentThroughTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
<?php

namespace Illuminate\Tests\Integration\Database\EloquentThroughTest;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Tests\Integration\Database\DatabaseTestCase;

class EloquentThroughTest extends DatabaseTestCase
{
protected function afterRefreshingDatabase()
{
Schema::create('posts', function (Blueprint $table) {
$table->increments('id');
$table->boolean('public');
});

Schema::create('other_commentables', function (Blueprint $table) {
$table->increments('id');
});

Schema::create('comments', function (Blueprint $table) {
$table->increments('id');
$table->string('commentable_type');
$table->integer('commentable_id');
});

Schema::create('likes', function (Blueprint $table) {
$table->increments('id');
$table->unsignedInteger('comment_id');
});


$post = tap(new Post(['public' => true]))->save();
$comment = tap((new Comment)->commentable()->associate($post))->save();
(new Like())->comment()->associate($comment)->save();
(new Like())->comment()->associate($comment)->save();

$otherCommentable = tap(new OtherCommentable())->save();
$comment2 = tap((new Comment)->commentable()->associate($otherCommentable))->save();
(new Like())->comment()->associate($comment2)->save();
}

public function test()
{
/** @var Post $post */
$post = Post::first();
$this->assertEquals(2, $post->commentLikes()->count());
}
}

class Comment extends Model
{
public $timestamps = false;

public function commentable()
{
return $this->morphTo();
}

public function likes()
{
return $this->hasMany(Like::class);
}
}

class Post extends Model
{
public $timestamps = false;

protected $guarded = [];

protected $withCount = ['comments'];

public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}

public function commentLikes()
{
return $this->through($this->comments())->has('likes');
}

public function texts()
{
return $this->hasMany(Text::class);
}
}

class OtherCommentable extends Model
{
public $timestamps = false;

public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}
}

class Text extends Model
{
public $timestamps = false;

protected $guarded = [];

public function post()
{
return $this->belongsTo(Post::class);
}
}

class Like extends Model
{
public $timestamps = false;

public function comment()
{
return $this->belongsTo(Comment::class);
}
}

0 comments on commit 702cb88

Please sign in to comment.