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; } }