Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

pkabore's avatar

How to query nested eloquent models using slugs

Hi everyone! I have three models with the following structure:

1. Course
    2.	Chapter
        3. Lesson

I want to have friendly urls so I used the getRouteKeyName method to set slug on each model for route model binding: My problem is that when I start querying for lessons using the chapter slug, I get lessons from other courses which have the same chapter slug.

Route::get('/{course}/{chapter}', [AppController::class, 'showLessons']);//List all the lesson

Is there a way to scope the query, with the course's slug and chapter's slug without having to fetch the parent Course and Chapter? Given that the slug can be changed later, I thought it would not be a good idea to add a course_slug column on the lessons table as this will necessitate the update of all related lessons' slug.

Thanks

0 likes
3 replies
pkabore's avatar

Thanks @tykus I've tried what you've recommended, It works. Now, my concern is that Laravel needs to resolve Course, then Chapter, and the relationships before retrieving for example Lesson. I thought it is too much? to have those queries and tried something like the following:

Route::get('/{course}/{chapter}/{lesson}', [AppController::class, 'showLesson'])->scopeBindings();

// in AppController.php
public function showLesson($course, $chapter, $lesson)
{
        $lesson = Lesson::where('course_slug', $course)
                            ->where('chapter_slug', $chapter)
                            ->where('lesson_slug', $lesson)
                            ->get();

        return $lesson;
}

with course_slug and chapter_slug not being foreign keys (because setting them as primary keys entails i wouldn't be able to update them) how can I enforce reference constraints. Thanks

1 like
tykus's avatar

@pkabore using whereHas (assuming (i) you have relations between Lesson and Chapter and Course and (ii) each model has a slug column not model_slug):

public function showLesson($course, $chapter, $lesson)
{
    return Lesson::where('slug', $lesson)
        ->whereHas('chapter', function ($chapterBuilder) use ($course, $chapter) {
            $chapterBuilder->where('slug', $chapter)
			    ->whereHas('course', fn ($courseBuilder) => $courseBuilder->where('slug', $course);
	})->first();
}

Or using joins:

public function showLesson($course, $chapter, $lesson)
{
    return Lesson::where('slug', $lesson)
        ->join('chapters', function ($join) use ($chapter) {
            $join->on('chapters.id', 'lessons.chapter_id')->where('chapters.slug', $chapter);
        ->join('courses', function ($join) use ($course) {
            $join->on('courses.id', 'chapters.course_id')->where('courses.slug', $chapter);
	})->first();
}
1 like

Please or to participate in this conversation.