Skip to content

Commit

Permalink
[11.x] Added inverse() to HasOne and HasMany
Browse files Browse the repository at this point in the history
  • Loading branch information
samlev committed May 31, 2024
1 parent 875d2b0 commit 85da77e
Show file tree
Hide file tree
Showing 12 changed files with 1,259 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
namespace Illuminate\Database\Eloquent\Relations\Concerns;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\RelationNotFoundException;

trait SupportsInverseRelations
{
protected string|null $inverseRelationship = null;

/**
* Gets the name of the inverse relationship
* Gets the name of the inverse relationship.
*
* @return string|null
*/
Expand All @@ -19,13 +20,17 @@ public function getInverseRelationship()
}

/**
* Links the related models back to the parent after the query has run
* Links the related models back to the parent after the query has run.
*
* @param string $relation
* @return $this
*/
public function inverse(string $relation)
{
if (! $this->getModel()->isRelation($relation)) {
throw RelationNotFoundException::make($this->getModel(), $relation);
}

if ($this->inverseRelationship === null && $relation) {
$this->query->afterQuery(function ($result) {
return $this->inverseRelationship
Expand All @@ -39,6 +44,18 @@ public function inverse(string $relation)
return $this;
}

/**
* Removes the inverse relationship for this query.
*
* @return $this
*/
public function withoutInverse()
{
$this->inverseRelationship = null;

return $this;
}

/**
* @param \Illuminate\Database\Eloquent\Collection $models
* @param \Illuminate\Database\Eloquent\Model|null $parent
Expand Down
17 changes: 12 additions & 5 deletions src/Illuminate/Database/Eloquent/Relations/HasMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ class HasMany extends HasOneOrMany
*/
public function one()
{
return HasOne::noConstraints(fn () => new HasOne(
$this->getQuery(),
$this->parent,
$this->foreignKey,
$this->localKey
return HasOne::noConstraints(fn () => tap(
new HasOne(
$this->getQuery(),
$this->parent,
$this->foreignKey,
$this->localKey
),
function ($hasOne) {
if ($inverse = $this->getInverseRelationship()) {
$hasOne->inverse($inverse);
}
}
));
}

Expand Down
7 changes: 4 additions & 3 deletions src/Illuminate/Database/Eloquent/Relations/HasOne.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,10 @@ public function addOneOfManyJoinSubQueryConstraints(JoinClause $join)
*/
public function newRelatedInstanceFor(Model $parent)
{
return $this->related->newInstance()->setAttribute(
$this->getForeignKeyName(), $parent->{$this->localKey}
);
return tap($this->related->newInstance(), function ($instance) use ($parent) {
$instance->setAttribute($this->getForeignKeyName(), $parent->{$this->localKey});
$this->applyInverseRelationToModel($instance, $parent);
});
}

/**
Expand Down
19 changes: 14 additions & 5 deletions src/Illuminate/Database/Eloquent/Relations/HasOneOrMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Concerns\InteractsWithDictionary;
use Illuminate\Database\Eloquent\Relations\Concerns\SupportsInverseRelations;
use Illuminate\Database\UniqueConstraintViolationException;

abstract class HasOneOrMany extends Relation
{
use InteractsWithDictionary;
use InteractsWithDictionary, SupportsInverseRelations;

/**
* The foreign key of the parent model.
Expand Down Expand Up @@ -53,6 +54,7 @@ public function make(array $attributes = [])
{
return tap($this->related->newInstance($attributes), function ($instance) {
$this->setForeignAttributesForCreate($instance);
$this->applyInverseRelationToModel($instance);
});
}

Expand Down Expand Up @@ -151,9 +153,13 @@ protected function matchOneOrMany(array $models, Collection $results, $relation,
// matching very convenient and easy work. Then we'll just return them.
foreach ($models as $model) {
if (isset($dictionary[$key = $this->getDictionaryKey($model->getAttribute($this->localKey))])) {
$model->setRelation(
$relation, $this->getRelationValue($dictionary, $key, $type)
);
$related = $this->getRelationValue($dictionary, $key, $type);
$model->setRelation($relation, $related);

// Apply the inverse relation if we have one
$type === 'one'
? $this->applyInverseRelationToModel($related, $model)
: $this->applyInverseRelationToCollection($related, $model);
}
}

Expand Down Expand Up @@ -340,6 +346,8 @@ public function create(array $attributes = [])
$this->setForeignAttributesForCreate($instance);

$instance->save();

$this->applyInverseRelationToModel($instance);
});
}

Expand All @@ -364,7 +372,7 @@ public function forceCreate(array $attributes = [])
{
$attributes[$this->getForeignKeyName()] = $this->getParentKey();

return $this->related->forceCreate($attributes);
return $this->applyInverseRelationToModel($this->related->forceCreate($attributes));
}

/**
Expand Down Expand Up @@ -415,6 +423,7 @@ public function createManyQuietly(iterable $records)
protected function setForeignAttributesForCreate(Model $model)
{
$model->setAttribute($this->getForeignKeyName(), $this->getParentKey());
$this->applyInverseRelationToModel($model);
}

/**
Expand Down
19 changes: 13 additions & 6 deletions src/Illuminate/Database/Eloquent/Relations/MorphMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,19 @@ class MorphMany extends MorphOneOrMany
*/
public function one()
{
return MorphOne::noConstraints(fn () => new MorphOne(
$this->getQuery(),
$this->getParent(),
$this->morphType,
$this->foreignKey,
$this->localKey
return MorphOne::noConstraints(fn () => tap(
new MorphOne(
$this->getQuery(),
$this->getParent(),
$this->morphType,
$this->foreignKey,
$this->localKey
),
function ($morphOne) {
if ($inverse = $this->getInverseRelationship()) {
$morphOne->inverse($inverse);
}
}
));
}

Expand Down
9 changes: 6 additions & 3 deletions src/Illuminate/Database/Eloquent/Relations/MorphOne.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,9 +119,12 @@ public function addOneOfManyJoinSubQueryConstraints(JoinClause $join)
*/
public function newRelatedInstanceFor(Model $parent)
{
return $this->related->newInstance()
->setAttribute($this->getForeignKeyName(), $parent->{$this->localKey})
->setAttribute($this->getMorphType(), $this->morphClass);
return tap($this->related->newInstance(), function ($instance) use ($parent) {
$instance->setAttribute($this->getForeignKeyName(), $parent->{$this->localKey})
->setAttribute($this->getMorphType(), $this->morphClass);

$this->applyInverseRelationToModel($instance, $parent);
});
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ public function forceCreate(array $attributes = [])
$attributes[$this->getForeignKeyName()] = $this->getParentKey();
$attributes[$this->getMorphType()] = $this->morphClass;

return $this->related->forceCreate($attributes);
return $this->applyInverseRelationToModel($this->related->forceCreate($attributes));
}

/**
Expand All @@ -92,6 +92,8 @@ protected function setForeignAttributesForCreate(Model $model)
$model->{$this->getForeignKeyName()} = $this->getParentKey();

$model->{$this->getMorphType()} = $this->morphClass;

$this->applyInverseRelationToModel($model);
}

/**
Expand Down
Loading

0 comments on commit 85da77e

Please sign in to comment.