Laravel many-to-many-to-many throught relationship

Hej guys,

currently I am developing a Laravel web app with a kind of hard to solve through-relationship. My relationship looks like this:

enter image description here

The problem is: I want to have a courses relationship on my module model. I tried to solve it with a custom has-many relationship that works ... kind of. Well, yes, it works, but only if I access it directly. If I tried to use this relationship with something like whereHas('courses') it fails and builds an incorrect query. I can work around this by using whereHas('module_numbers.courses'), but actually, that's not the way I want it to be – if there is a better solution, that I haven't found yet.

Any pro ideas on how to solve my problem?

My current "solution":

public function courses() {
  $query = (
    Course::query()
      ->select('courses.*')
      ->join(
        "course_module_number",
        "courses.id",
        "course_module_number.course_id"
      )
      ->join(
        "module_numbers",
        "course_module_number.module_number_id",
        "module_numbers.id"
      )
      ->join(
        "modules",
        "module_numbers.module_id",
        "modules.id"
      )
      ->distinct()
  );

  return $this->newHasMany(
    $query,
    $this,
    $this->getForeignKey(),
    $this->getKeyName()
  );
}

EDIT (2022-01-17): changed database schema: enter image description here


Solution 1:

I would solve this as following

In your Course model

//import Module model

public function modules(){
  return $this->belongsToMany(Module::class);
}

In your Module model

//import Course model

public function courses(){
  return $this->belongsToMany(Course::class);
}

Now you can access using eagerloading as follows

$courses = Course::with('modules')->get();
$modules = Module::with('courses')->get();

OR

use it with has if you want get as with at least one data

$courses = Course::has('modules')->get();
$modules = Module::has('courses')->get();

Now you can use foreach or if you want to access relationship you can do following

 foreach($courses as $key => $value){
   $value->modules->credit_points
 }

OR for module

 foreach($modules as $key => $value){
   $value->courses->semester
 }