Skip to content

Commit

Permalink
Correctly load nested relationships for polymorphic relations
Browse files Browse the repository at this point in the history
  • Loading branch information
phroggyy committed May 27, 2016
1 parent d8791b9 commit 811cee7
Show file tree
Hide file tree
Showing 2 changed files with 248 additions and 0 deletions.
17 changes: 17 additions & 0 deletions src/Illuminate/Database/Eloquent/Relations/MorphTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Collection as BaseCollection;
use Illuminate\Support\Str;

class MorphTo extends BelongsTo
{
Expand Down Expand Up @@ -176,6 +178,8 @@ protected function getResultsByType($type)
$key = $instance->getTable().'.'.$instance->getKeyName();

$query = clone $this->query;

$query->setEagerLoads($this->getEagerLoadsForInstance($instance));
$query->setModel($instance);

return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get();
Expand Down Expand Up @@ -210,6 +214,19 @@ public function createModelByType($type)
return new $class;
}

protected function getEagerLoadsForInstance(Model $instance)
{
$eagers = BaseCollection::make($this->query->getEagerLoads());

$eagers = $eagers->filter(function ($constraint, $relation) {
return Str::startsWith($relation, $this->relation.'.');
});

return $eagers->keys()->map(function ($key) {
return Str::replaceFirst($this->relation.'.', '', $key);
})->combine($eagers)->merge($instance->getEagerLoads())->all();
}

/**
* Get the foreign key "type" name.
*
Expand Down
231 changes: 231 additions & 0 deletions tests/Database/DatabaseEloquentPolymorphicIntegrationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
<?php

use Carbon\Carbon;
use Illuminate\Database\Connection;
use Illuminate\Pagination\Paginator;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Capsule\Manager as DB;
use Illuminate\Database\Eloquent\SoftDeletingScope;
use Illuminate\Database\Eloquent\Model as Eloquent;

class DatabaseEloquentPolymorphicIntegrationTest extends PHPUnit_Framework_TestCase
{
public function setUp()
{
$db = new DB;

$db->addConnection([
'driver' => 'sqlite',
'database' => ':memory:',
]);

$db->bootEloquent();
$db->setAsGlobal();

$this->createSchema();
}

/**
* Setup the database schema.
*
* @return void
*/
public function createSchema()
{
$this->schema()->create('users', function ($table) {
$table->increments('id');
$table->string('email')->unique();
$table->timestamps();
});

$this->schema()->create('posts', function ($table) {
$table->increments('id');
$table->integer('user_id');
$table->string('title');
$table->text('body');
$table->timestamps();
});

$this->schema()->create('comments', function ($table) {
$table->increments('id');
$table->integer('commentable_id');
$table->string('commentable_type');
$table->integer('user_id');
$table->text('body');
$table->timestamps();
});

$this->schema()->create('likes', function ($table) {
$table->increments('id');
$table->integer('likeable_id');
$table->string('likeable_type');
$table->timestamps();
});
}

/**
* Tear down the database schema.
*
* @return void
*/
public function tearDown()
{
$this->schema()->drop('users');
$this->schema()->drop('posts');
$this->schema()->drop('comments');
}

public function testItLoadsRelationshipsAutomatically()
{
$this->createUsers();

$taylor = TestUser::first();
$taylor->posts()->create(['title' => 'A title', 'body' => 'A body'])
->comments()->create(['body' => 'A comment body', 'user_id' => 1])
->likes()->create([]);

$like = TestLikeWithSingleWith::first();

$this->assertTrue($like->relationLoaded('likeable'));

$this->assertEquals(TestComment::first(), $like->likeable);
}

public function testItLoadsNestedRelationshipsAutomatically()
{
$this->createUsers();

$taylor = TestUser::first();
$taylor->posts()->create(['title' => 'A title', 'body' => 'A body'])
->comments()->create(['body' => 'A comment body', 'user_id' => 1])
->likes()->create([]);

$like = TestLikeWithNestedWith::first();

$this->assertTrue($like->relationLoaded('likeable'));
$this->assertTrue($like->likeable->relationLoaded('owner'));

$this->assertEquals($taylor, $like->likeable->owner);
}

/**
* Helpers...
*/
protected function createUsers()
{
TestUser::create(['id' => 1, 'email' => 'taylorotwell@gmail.com']);
}

/**
* Get a database connection instance.
*
* @return Connection
*/
protected function connection()
{
return Eloquent::getConnectionResolver()->connection();
}

/**
* Get a schema builder instance.
*
* @return Schema\Builder
*/
protected function schema()
{
return $this->connection()->getSchemaBuilder();
}
}

/**
* Eloquent Models...
*/
class TestUser extends Eloquent
{
protected $table = 'users';
protected $guarded = [];

public function posts()
{
return $this->hasMany(TestPost::class, 'user_id');
}
}

/**
* Eloquent Models...
*/
class TestPost extends Eloquent
{
protected $table = 'posts';
protected $guarded = [];

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

public function owner()
{
return $this->belongsTo(TestUser::class, 'user_id');
}
}

/**
* Eloquent Models...
*/
class TestComment extends Eloquent
{
protected $table = 'comments';
protected $guarded = [];

public function owner()
{
return $this->belongsTo(TestUser::class, 'user_id');
}

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

public function likes()
{
return $this->morphMany(TestLike::class, 'likeable');
}
}

class TestLike extends Eloquent
{
protected $table = 'likes';
protected $guarded = [];

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

class TestLikeWithSingleWith extends Eloquent
{
protected $table = 'likes';
protected $guarded = [];
protected $with = ['likeable'];

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

class TestLikeWithNestedWith extends Eloquent
{
protected $table = 'likes';
protected $guarded = [];
protected $with = ['likeable.owner'];

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

0 comments on commit 811cee7

Please sign in to comment.