I will show you another type of relationship: has many through. It's just a fancy word for defining two-level-deep has-many relationships.
Imagine a scenario where you have a user with many projects, and then you have projects with many tasks.
And you want to show the users their own tasks, bypassing the projects. But to get to the tasks, you still need to load the projects, right?
So, for each user, you perform a loop through projects and then a loop through tasks. Then, you will show a description of the task.
$users = User::with('projects.tasks')->get(); foreach ($users as $user) { print '<div><strong>' . $user->id . ': ' . $user->name . '</strong></div>'; foreach ($user->projects as $project) { foreach ($project->tasks as $task) { print $task->description .'... '; } } print '<hr />';}
But there's a shorter way. Using the model, we can define a Has Many Through relation. The hasManyThrough()
first accepts the class of which records we want to get, and the second parameter is the intermediate class. So, we want to get tasks through projects.
use Illuminate\Database\Eloquent\Relations\HasMany;use Illuminate\Database\Eloquent\Relations\HasManyThrough; class User extends Authenticatable{ // ... public function projects(): HasMany { return $this->hasMany(Project::class); } public function tasks(): HasManyThrough { return $this->hasManyThrough(Task::class, Project::class); }}
We can simplify the Controller. We can eagerly load the tasks directly and remove additional foreach loop.
$users = User::with('tasks')->get(); foreach ($users as $user) { print '<div><strong>' . $user->id . ': ' . $user->name . '</strong></div>'; foreach ($user->tasks as $task) { print $task->description .'... '; } print '<hr />';}
And the result is the same.
So, hasManyThrough()
will query the tasks, which will simplify the query because you don't need the projects in this case. You need the parent and kind of the grandchild of that parent.