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
Please or to participate in this conversation.