248 lines
5.1 KiB
PHP
248 lines
5.1 KiB
PHP
|
<?php namespace Illuminate\Database\Eloquent\Relations;
|
||
|
|
||
|
use Illuminate\Database\Eloquent\Model;
|
||
|
use Illuminate\Database\Eloquent\Builder;
|
||
|
use Illuminate\Database\Eloquent\Collection;
|
||
|
use Illuminate\Support\Collection as BaseCollection;
|
||
|
|
||
|
class MorphTo extends BelongsTo {
|
||
|
|
||
|
/**
|
||
|
* The type of the polymorphic relation.
|
||
|
*
|
||
|
* @var string
|
||
|
*/
|
||
|
protected $morphType;
|
||
|
|
||
|
/**
|
||
|
* The models whose relations are being eager loaded.
|
||
|
*
|
||
|
* @var \Illuminate\Database\Eloquent\Collection
|
||
|
*/
|
||
|
protected $models;
|
||
|
|
||
|
/**
|
||
|
* All of the models keyed by ID.
|
||
|
*
|
||
|
* @var array
|
||
|
*/
|
||
|
protected $dictionary = array();
|
||
|
|
||
|
/*
|
||
|
* Indicates if soft-deleted model instances should be fetched.
|
||
|
*
|
||
|
* @var bool
|
||
|
*/
|
||
|
protected $withTrashed = false;
|
||
|
|
||
|
/**
|
||
|
* Create a new morph to relationship instance.
|
||
|
*
|
||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||
|
* @param \Illuminate\Database\Eloquent\Model $parent
|
||
|
* @param string $foreignKey
|
||
|
* @param string $otherKey
|
||
|
* @param string $type
|
||
|
* @param string $relation
|
||
|
* @return void
|
||
|
*/
|
||
|
public function __construct(Builder $query, Model $parent, $foreignKey, $otherKey, $type, $relation)
|
||
|
{
|
||
|
$this->morphType = $type;
|
||
|
|
||
|
parent::__construct($query, $parent, $foreignKey, $otherKey, $relation);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Set the constraints for an eager load of the relation.
|
||
|
*
|
||
|
* @param array $models
|
||
|
* @return void
|
||
|
*/
|
||
|
public function addEagerConstraints(array $models)
|
||
|
{
|
||
|
$this->buildDictionary($this->models = Collection::make($models));
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Build a dictionary with the models.
|
||
|
*
|
||
|
* @param \Illuminate\Database\Eloquent\Collection $models
|
||
|
* @return void
|
||
|
*/
|
||
|
protected function buildDictionary(Collection $models)
|
||
|
{
|
||
|
foreach ($models as $model)
|
||
|
{
|
||
|
if ($model->{$this->morphType})
|
||
|
{
|
||
|
$this->dictionary[$model->{$this->morphType}][$model->{$this->foreignKey}][] = $model;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Match the eagerly loaded results to their parents.
|
||
|
*
|
||
|
* @param array $models
|
||
|
* @param \Illuminate\Database\Eloquent\Collection $results
|
||
|
* @param string $relation
|
||
|
* @return array
|
||
|
*/
|
||
|
public function match(array $models, Collection $results, $relation)
|
||
|
{
|
||
|
return $models;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Associate the model instance to the given parent.
|
||
|
*
|
||
|
* @param \Illuminate\Database\Eloquent\Model $model
|
||
|
* @return \Illuminate\Database\Eloquent\Model
|
||
|
*/
|
||
|
public function associate(Model $model)
|
||
|
{
|
||
|
$this->parent->setAttribute($this->foreignKey, $model->getKey());
|
||
|
|
||
|
$this->parent->setAttribute($this->morphType, $model->getMorphClass());
|
||
|
|
||
|
return $this->parent->setRelation($this->relation, $model);
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the results of the relationship.
|
||
|
*
|
||
|
* Called via eager load method of Eloquent query builder.
|
||
|
*
|
||
|
* @return mixed
|
||
|
*/
|
||
|
public function getEager()
|
||
|
{
|
||
|
foreach (array_keys($this->dictionary) as $type)
|
||
|
{
|
||
|
$this->matchToMorphParents($type, $this->getResultsByType($type));
|
||
|
}
|
||
|
|
||
|
return $this->models;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Match the results for a given type to their parents.
|
||
|
*
|
||
|
* @param string $type
|
||
|
* @param \Illuminate\Database\Eloquent\Collection $results
|
||
|
* @return void
|
||
|
*/
|
||
|
protected function matchToMorphParents($type, Collection $results)
|
||
|
{
|
||
|
foreach ($results as $result)
|
||
|
{
|
||
|
if (isset($this->dictionary[$type][$result->getKey()]))
|
||
|
{
|
||
|
foreach ($this->dictionary[$type][$result->getKey()] as $model)
|
||
|
{
|
||
|
$model->setRelation($this->relation, $result);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get all of the relation results for a type.
|
||
|
*
|
||
|
* @param string $type
|
||
|
* @return \Illuminate\Database\Eloquent\Collection
|
||
|
*/
|
||
|
protected function getResultsByType($type)
|
||
|
{
|
||
|
$instance = $this->createModelByType($type);
|
||
|
|
||
|
$key = $instance->getKeyName();
|
||
|
|
||
|
$query = $instance->newQuery();
|
||
|
|
||
|
$query = $this->useWithTrashed($query);
|
||
|
|
||
|
return $query->whereIn($key, $this->gatherKeysByType($type)->all())->get();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Gather all of the foreign keys for a given type.
|
||
|
*
|
||
|
* @param string $type
|
||
|
* @return array
|
||
|
*/
|
||
|
protected function gatherKeysByType($type)
|
||
|
{
|
||
|
$foreign = $this->foreignKey;
|
||
|
|
||
|
return BaseCollection::make($this->dictionary[$type])->map(function($models) use ($foreign)
|
||
|
{
|
||
|
return head($models)->{$foreign};
|
||
|
|
||
|
})->unique();
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Create a new model instance by type.
|
||
|
*
|
||
|
* @param string $type
|
||
|
* @return \Illuminate\Database\Eloquent\Model
|
||
|
*/
|
||
|
public function createModelByType($type)
|
||
|
{
|
||
|
return new $type;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the foreign key "type" name.
|
||
|
*
|
||
|
* @return string
|
||
|
*/
|
||
|
public function getMorphType()
|
||
|
{
|
||
|
return $this->morphType;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Get the dictionary used by the relationship.
|
||
|
*
|
||
|
* @return array
|
||
|
*/
|
||
|
public function getDictionary()
|
||
|
{
|
||
|
return $this->dictionary;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fetch soft-deleted model instances with query.
|
||
|
*
|
||
|
* @return $this
|
||
|
*/
|
||
|
public function withTrashed()
|
||
|
{
|
||
|
$this->withTrashed = true;
|
||
|
|
||
|
$this->query = $this->useWithTrashed($this->query);
|
||
|
|
||
|
return $this;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Return trashed models with query if told so.
|
||
|
*
|
||
|
* @param \Illuminate\Database\Eloquent\Builder $query
|
||
|
* @return \Illuminate\Database\Eloquent\Builder
|
||
|
*/
|
||
|
protected function useWithTrashed(Builder $query)
|
||
|
{
|
||
|
if ($this->withTrashed && $query->getMacro('withTrashed') !== null)
|
||
|
{
|
||
|
return $query->withTrashed();
|
||
|
}
|
||
|
|
||
|
return $query;
|
||
|
}
|
||
|
|
||
|
}
|