Skip to content

Commit

Permalink
[10.x] Allow for converting a HasMany to HasOne && MorphMany to Morph…
Browse files Browse the repository at this point in the history
…One (#46443)

* allow for converting a HasMany to HasOne && MorphMany to MorphOne

* fixes model names for EloquentHasManyTest

* style fixes

* formatting

* rename method

---------

Co-authored-by: Taylor Otwell <[email protected]>
  • Loading branch information
cosmastech and taylorotwell authored Mar 13, 2023
1 parent 3397861 commit 38f022d
Show file tree
Hide file tree
Showing 5 changed files with 150 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/Illuminate/Database/Eloquent/Relations/HasMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@

class HasMany extends HasOneOrMany
{
/**
* Convert the relationship to a "has one" relationship.
*
* @return Illuminate\Database\Eloquent\Relations\HasOne
*/
public function one()
{
return new HasOne($this->getQuery(), $this->parent, $this->foreignKey, $this->localKey);
}

/**
* Get the results of the relationship.
*
Expand Down
18 changes: 18 additions & 0 deletions src/Illuminate/Database/Eloquent/Relations/HasManyThrough.php
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,24 @@ public function __construct(Builder $query, Model $farParent, Model $throughPare
parent::__construct($query, $throughParent);
}

/**
* Convert the relationship to a "has one through" relationship.
*
* @return \Illuminate\Database\Eloquent\Relations\HasOneThrough
*/
public function one()
{
return new HasOneThrough(
$this->getQuery(),
$this->farParent,
$this->throughParent,
$this->getFirstKeyName(),
$this->secondKey,
$this->getLocalKeyName(),
$this->getSecondLocalKeyName(),
);
}

/**
* Set the base constraints on the relation query.
*
Expand Down
10 changes: 10 additions & 0 deletions src/Illuminate/Database/Eloquent/Relations/MorphMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@

class MorphMany extends MorphOneOrMany
{
/**
* Convert the relationship to a "morph one" relationship.
*
* @return \Illuminate\Database\Eloquent\Relations\MorphOne
*/
public function one()
{
return new MorphOne($this->getQuery(), $this->getParent(), $this->morphType, $this->foreignKey, $this->localKey);
}

/**
* Get the results of the relationship.
*
Expand Down
81 changes: 81 additions & 0 deletions tests/Integration/Database/EloquentHasManyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace Illuminate\Tests\Integration\Database;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Support\Facades\Schema;

class EloquentHasManyTest extends DatabaseTestCase
{
protected function defineDatabaseMigrationsAfterDatabaseRefreshed()
{
Schema::create('eloquent_has_many_test_users', function ($table) {
$table->id();
});

Schema::create('eloquent_has_many_test_logins', function ($table) {
$table->id();
$table->foreignId('eloquent_has_many_test_user_id');
$table->timestamp('login_time');
});
}

public function testCanGetHasOneFromHasManyRelationship()
{
$user = EloquentHasManyTestUser::create();

$user->logins()->create(['login_time' => now()]);

$this->assertInstanceOf(HasOne::class, $user->logins()->one());
}

public function testHasOneRelationshipFromHasMany()
{
$user = EloquentHasManyTestUser::create();

EloquentHasManyTestLogin::create([
'eloquent_has_many_test_user_id' => $user->id,
'login_time' => '2020-09-29',
]);
$latestLogin = EloquentHasManyTestLogin::create([
'eloquent_has_many_test_user_id' => $user->id,
'login_time' => '2023-03-14',
]);
$oldestLogin = EloquentHasManyTestLogin::create([
'eloquent_has_many_test_user_id' => $user->id,
'login_time' => '2010-01-01',
]);

$this->assertEquals($oldestLogin->id, $user->oldestLogin->id);
$this->assertEquals($latestLogin->id, $user->latestLogin->id);
}
}

class EloquentHasManyTestUser extends Model
{
protected $guarded = [];
public $timestamps = false;

public function logins(): HasMany
{
return $this->hasMany(EloquentHasManyTestLogin::class);
}

public function latestLogin(): HasOne
{
return $this->logins()->one()->latestOfMany('login_time');
}

public function oldestLogin(): HasOne
{
return $this->logins()->one()->oldestOfMany('login_time');
}
}

class EloquentHasManyTestLogin extends Model
{
protected $guarded = [];
public $timestamps = false;
}
31 changes: 31 additions & 0 deletions tests/Integration/Database/EloquentMorphManyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
namespace Illuminate\Tests\Integration\Database\EloquentMorphManyTest;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;
use Illuminate\Tests\Integration\Database\DatabaseTestCase;
Expand Down Expand Up @@ -48,6 +50,25 @@ public function test_self_referencing_existence_query()

$this->assertEquals([1], $comments->pluck('id')->all());
}

public function testCanMorphOne()
{
$post = Post::create(['title' => 'Your favorite book by C.S. Lewis']);

Carbon::setTestNow('1990-02-02 12:00:00');
$oldestComment = tap((new Comment(['name' => 'The Allegory Of Love']))->commentable()->associate($post))->save();

Carbon::setTestNow('2000-07-02 09:00:00');
tap((new Comment(['name' => 'The Screwtape Letters']))->commentable()->associate($post))->save();

Carbon::setTestNow('2022-01-01 00:00:00');
$latestComment = tap((new Comment(['name' => 'The Silver Chair']))->commentable()->associate($post))->save();

$this->assertInstanceOf(MorphOne::class, $post->comments()->one());

$this->assertEquals($latestComment->id, $post->latestComment->id);
$this->assertEquals($oldestComment->id, $post->oldestComment->id);
}
}

class Post extends Model
Expand All @@ -61,6 +82,16 @@ public function comments()
{
return $this->morphMany(Comment::class, 'commentable');
}

public function latestComment(): MorphOne
{
return $this->comments()->one()->latestOfMany();
}

public function oldestComment(): MorphOne
{
return $this->comments()->one()->oldestOfMany();
}
}

class Comment extends Model
Expand Down

0 comments on commit 38f022d

Please sign in to comment.