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

bhuether's avatar

How would you do this tricky JOIN query?

I have a guitar lessons site (laravel 5.2) where lessons contain exercises. In exercise table you have lessonid as well as a difficulty. For lessons table, there is no difficulty column, as difficulty is determined dynamically by retrieving maximum difficulty of the exercises in the lesson.

So, what I need to do is sort lessons on difficulty. Here are the functions involved:

public function searchLessons($parameters)
    {
       
        $this->parameters = $parameters;
    $query = \App\Lesson::query();
        $query = $this->filterLessons($query);
        $query = $this->sortByLessonSimilarity($query);
        $query = $this->sortLessons($query);

        $count = $this->count($query);
        $pageInfo = $this->page($query);

        $lessons = $query->get()->load('exercises')->load('category')->load('coverPhoto');

        return [
            'lessons' => $lessons,
            'count' => $count,
            'page' => $pageInfo,
        ];
    }

Here is the filterlessons function:

private function filterLessons($query)
    {
        if (isset($this->parameters['keywords']))
        {
            $kw = preg_replace('/\W+/', '%', $this->parameters['keywords']);
            $query->where(function ($query) use ($kw) {
                $query->where('title', 'like', '%' . $kw . '%')
                      ->orWhere('description', 'like', '%' . $kw . '%')
                      ->orWhere('text', 'like', '%' . $kw . '%');

                $query->orWhereExists(function ($query) use ($kw) {
                    $query->select(DB::raw('1'))
                          ->from('categories')
                          ->whereRaw('id = lessons.category_id')
                          ->where('title', 'like', '%' . $kw . '%');
                });

                
                $query->orWhereExists(function ($query) use ($kw) {
                    $query->select(DB::raw('1'))
                          ->from('exercises')
                          ->whereRaw('lesson_id = lessons.id')
                          ->where(function ($query) use ($kw) {
                              $query->where('title', 'like', '%' . $kw . '%')
                                    ->orWhere('intro', 'like', '%' . $kw . '%')
                                    ->orWhere('text', 'like', '%' . $kw . '%');
                          });
                });
            });
        }
        if (isset($this->parameters['category']))
        {
            $query->wherein('category_id', (array)$this->parameters['category']);
        }
        if (isset($this->parameters['difficulty']))
        {
            $difficulty = intval($this->parameters['difficulty']);
            $query->whereExists(function ($query) use ($difficulty) {
                $query->select(DB::raw('max(difficulty) from exercises where lesson_id = lessons.id having max(difficulty) = ' . $difficulty));
            });
        }
        return $query;
    }

And the sortlessons function:

private function sortLessons($query)
    {
        if (isset($this->parameters['sort']))
        {
            switch ($this->parameters['sort'])
            {
                case 'popularity': $query->orderByRaw('(hits / (1 + datediff(now(), created_at))) desc'); break;
                case 'difficulty': break;
                case 'date_desc': $query->orderBy('created_at', 'desc'); break;
                case 'date_asc': $query->orderBy('created_at', 'asc'); break;
                default: throw new Exception('Unknown sort value.');
            }
        }
        else
        {
            $query->orderBy('created_at', 'desc');
        }
        return $query;
    }

Note that in the case of filtering by a given difficulty level, things work fine with the above lines in the filter function:

$difficulty = intval($this->parameters['difficulty']);
            $query->whereExists(function ($query) use ($difficulty) {
                $query->select(DB::raw('max(difficulty) from exercises where lesson_id = lessons.id having max(difficulty) = ' . $difficulty));

But I am not sure what to do to make things work when sorting by all difficulty levels (there are 4).

Any ideas?

Thanks!

Brian

0 likes
0 replies

Please or to participate in this conversation.