layfi-earn/vendor/illuminate/database/Eloquent/Relations/Concerns/SupportsInverseRelations.php
2025-01-27 20:49:47 +08:00

158 lines
4.3 KiB
PHP

<?php
namespace Illuminate\Database\Eloquent\Relations\Concerns;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\RelationNotFoundException;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
trait SupportsInverseRelations
{
/**
* The name of the inverse relationship.
*
* @var string|null
*/
protected ?string $inverseRelationship = null;
/**
* Instruct Eloquent to link the related models back to the parent after the relationship query has run.
*
* Alias of "chaperone".
*
* @param string|null $relation
* @return $this
*/
public function inverse(?string $relation = null)
{
return $this->chaperone($relation);
}
/**
* Instruct Eloquent to link the related models back to the parent after the relationship query has run.
*
* @param string|null $relation
* @return $this
*/
public function chaperone(?string $relation = null)
{
$relation ??= $this->guessInverseRelation();
if (! $relation || ! $this->getModel()->isRelation($relation)) {
throw RelationNotFoundException::make($this->getModel(), $relation ?: 'null');
}
if ($this->inverseRelationship === null && $relation) {
$this->query->afterQuery(function ($result) {
return $this->inverseRelationship
? $this->applyInverseRelationToCollection($result, $this->getParent())
: $result;
});
}
$this->inverseRelationship = $relation;
return $this;
}
/**
* Guess the name of the inverse relationship.
*
* @return string|null
*/
protected function guessInverseRelation(): ?string
{
return Arr::first(
$this->getPossibleInverseRelations(),
fn ($relation) => $relation && $this->getModel()->isRelation($relation)
);
}
/**
* Get the possible inverse relations for the parent model.
*
* @return array<non-empty-string>
*/
protected function getPossibleInverseRelations(): array
{
return array_filter(array_unique([
Str::camel(Str::beforeLast($this->getForeignKeyName(), $this->getParent()->getKeyName())),
Str::camel(Str::beforeLast($this->getParent()->getForeignKey(), $this->getParent()->getKeyName())),
Str::camel(class_basename($this->getParent())),
'owner',
get_class($this->getParent()) === get_class($this->getModel()) ? 'parent' : null,
]));
}
/**
* Set the inverse relation on all models in a collection.
*
* @param \Illuminate\Database\Eloquent\Collection $models
* @param \Illuminate\Database\Eloquent\Model|null $parent
* @return \Illuminate\Database\Eloquent\Collection
*/
protected function applyInverseRelationToCollection($models, ?Model $parent = null)
{
$parent ??= $this->getParent();
foreach ($models as $model) {
$model instanceof Model && $this->applyInverseRelationToModel($model, $parent);
}
return $models;
}
/**
* Set the inverse relation on a model.
*
* @param \Illuminate\Database\Eloquent\Model $model
* @param \Illuminate\Database\Eloquent\Model|null $parent
* @return \Illuminate\Database\Eloquent\Model
*/
protected function applyInverseRelationToModel(Model $model, ?Model $parent = null)
{
if ($inverse = $this->getInverseRelationship()) {
$parent ??= $this->getParent();
$model->setRelation($inverse, $parent);
}
return $model;
}
/**
* Get the name of the inverse relationship.
*
* @return string|null
*/
public function getInverseRelationship()
{
return $this->inverseRelationship;
}
/**
* Remove the chaperone / inverse relationship for this query.
*
* Alias of "withoutChaperone".
*
* @return $this
*/
public function withoutInverse()
{
return $this->withoutChaperone();
}
/**
* Remove the chaperone / inverse relationship for this query.
*
* @return $this
*/
public function withoutChaperone()
{
$this->inverseRelationship = null;
return $this;
}
}