Mike F
Mike F

Reputation: 368

Laravel get belongsToMany through hasMany

I have 3 models: Conference, Session and Speaker. A Conference can have many Sessions and many Sessions can have many Speakers.

Database structure

conferences
-----------
id
name


sessions
--------
id
conference_id
name


session_speaker
---------------
id
session_id
speaker_id


speakers
--------
id
name

I need to write a method which allows me to get all the Speakers of a particular Conference (so all the Speakers from all the Sessions of that particular Conference).

The following illustrates what I think should work, but it obviously doesn't as I can't chain these together.

app/models/Conference.php

class Conference extends Eloquent {

  public function speakers() {
    return $this->hasMany('Session')->belongsToMany('Speaker');
  }

}

I have all the Model-Model relationships setup and working correctly (Conference-Sessions, Session-Speakers) however I can't create the bridge between Conference-Sessions-Speakers. Does anyone know how I can achieve this without writing a large SQL join query?

I think if there was a relationship belongsToManyThrough() this would work however there isn't.

Thanks in advance!

Upvotes: 8

Views: 11591

Answers (3)

Gabriel Henrique
Gabriel Henrique

Reputation: 13

The best solution is use the lib "hasManyDeep" (https://github.com/staudenmeir/eloquent-has-many-deep)

With this you can create the complete relationship, independently of be "has one", 'has many" or "many to many". You can create something like:

return $this->hasManyDeep(
            Speaker::class,
            ['session_speaker', Session::class]
        );

You can analyse the SQL creat

Upvotes: 0

osama Abdullah
osama Abdullah

Reputation: 321

I think a cleaner way will be

public function speakers()
{
    return Speaker::whereHas('sessions', fn($query) => $query->where('conference_id', $this->id))->get();
}

but you should set up the belongsToMany() relationship between Session and Speaker Models

Upvotes: 1

Matthias S
Matthias S

Reputation: 3553

Unfortunately, the hasManyThrough relation does not work with many to many relationships in between.

What you can do is something like this:

public function speakers() {
  $session_ids = Session::where('conference_id', $this->id);
  $speaker_ids = DB::table('session_speaker')->whereIn('session_id', $session_ids)->lists('speaker_id');
  return Speaker::whereIn('id', $speaker_ids)->get();
}

You probably need to set the variables to array(0) if no results are found, otherwise the whereIn function will throw an error. You could also use an Eloquent-only way, but that would probably result in many more database queries, while this one should be fine with only 2 queries being run.

You can then access the speakers with e.g. Conference::find(1)->speakers().

Note from 2021: This answer is from back in 2014 and relates to Laravel 4. Nowadays, this apparently does not work anymore. I would encourage you to check the Laravel docs, maybe this problem can now be solved in a better way.

Upvotes: 10

Related Questions