To build database queries using Eloquent under the hood, Laravel uses Illuminate\Database\Eloquent\Builder instance.
When you call with method, you add a relation to eagetLoad array property of the builder:
public function with($relations, $callback = null)
{
if ($callback instanceof Closure) {
$eagerLoad = $this->parseWithRelations([$relations => $callback]);
} else {
$eagerLoad = $this->parseWithRelations(is_string($relations) ? func_get_args() : $relations);
}
$this->eagerLoad = array_merge($this->eagerLoad, $eagerLoad);
return $this;
}
Then when you try to receive a result using get, find, findOrFail, etc., builder calls eagerLoadRelation method:
protected function eagerLoadRelation(array $models, $name, Closure $constraints)
{
// First we will "back up" the existing where conditions on the query so we can
// add our eager constraints. Then we will merge the wheres that were on the
// query back to it in order that any where conditions might be specified.
$relation = $this->getRelation($name);
$relation->addEagerConstraints($models);
$constraints($relation);
// Once we have the results, we just match those back up to their parent models
// using the relationship instance. Then we just return the finished arrays
// of models which have been eagerly hydrated and are readied for return.
return $relation->match(
$relation->initRelation($models, $name),
$relation->getEager(), $name
);
}
The matching with parent models is happening within match method, which implemented differently for every relation type (you may see Illuminate\Database\Eloquent\Relations namespace). For most relations the matchOneOrMany method is used (dictionary is a simple collection, that filtered by foreign key for relation):
protected function matchOneOrMany(array $models, Collection $results, $relation, $type)
{
$dictionary = $this->buildDictionary($results);
// Once we have the dictionary we can simply spin through the parent models to
// link them up with their children using the keyed dictionary to make the
// 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)
);
}
}
return $models;
}