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

StuffedGoat's avatar

How to join 3 tables with Eloquent?

Hello everybody,

I'm trying to join 3 tables with Eloquent. I have three models pages, maintable and categories.

Categories:

    public function page()
    {
        return $this->hasMany('App\Page', 'category_fk');
    }
    
    public function maintable() 
    {
        return $this->belongsTo('App\Maintable', 'maintable_fk');
    }

Maintable:

    public function category()
    {
        return $this->hasMany('App\Category', 'maintable_fk');
    }

Pages:

   public function category() 
    {
        return $this->belongsTo('App\Category', 'id');
    }

Via SQL query it works with the following statement:

select  * 
from    pages p, maintables m, categories c 
where   m.id = c.maintable_fk and 
        c.id = p.category_fk

How can I convert this into Eloquent?

0 likes
8 replies
pmall's avatar

I think your category relationship of the Pages model is wrong, it should be :

public function category() 
{
    return $this->belongsTo('App\Category', 'category_fk');
}

And your has many relationship names should be pluralized. $maintable->categories and $category->pages.

Finally no need to join anything, just use the relations.

$maintable = Maintable::with('categories.pages')->get();

foreach ($maintable->categories as $category)
{
    foreach ($category->pages as $page)
    {
        // $maintable->id, $category->id, $page->id
    }
}
1 like
StuffedGoat's avatar

@pmall I tried to implement your proposal but it didn't work but I can feel that you are very close. And thanks for the advice with the plural-syntax - I will implement this!

GitHub Logo

StuffedGoat's avatar

@pmall I'm a little further right now. You above solution works but I can't get the foreach working. I also renamed the methods in the models so they become pluralized. This is the output:

Undefined property: Illuminate\Database\Eloquent\Builder::$categories
        $maintable = Maintable::with('categories.pages')->get();
        
        foreach ($maintable->categories as $category)
        {
            foreach ($category->pages as $page)
            {
                return $maintable->id;
            }
        }
pmall's avatar

You use category whereas your relationship is named category.

It is easy, always pluralize the names of has many relationships and singularize the names of belongs to relationship

1 like
StuffedGoat's avatar

I really did exactly what you suggested/mentioned.

This is what I right now copied from my models:

// Category - Model
    public function pages()
    {
        return $this->hasMany('App\Page', 'category_fk');
    }
    
    public function maintable() 
    {
        return $this->belongsTo('App\Maintable', 'maintable_fk');
    }   

// Maintable - Model
    public function categories()
    {
        return $this->hasMany('App\Category', 'maintable_fk');
    }

// Page - Model
    public function category() 
    {
        return $this->belongsTo('App\Category', 'category_fk');
    }
// This is your method where I only added a return statement
        foreach ($maintable->categories as $category)
        {
            foreach ($category->pages as $page)
            {
                return $maintable->id;
            }
        }

And the error message:

Undefined property: Illuminate\Database\Eloquent\Collection::$categories
phildawson's avatar
Level 26

@StuffedGoat Yeah the ->get will return a Collection of Maintable Models. Here you are saying get me all the maintables and eager load the categories and its pages.

$maintables = Maintable::with('categories.pages')->get(); // Collection

foreach ($maintables as $maintable) {

    foreach ($maintable->categories as $category) {

        foreach ($category->pages as $page) {

        }
    }
}
1 like
pmall's avatar

Yes I forgot a level of foreach, you have to loop over the maintables too. Try to understand the examples we provide.

1 like
StuffedGoat's avatar

So.. thank you very much gentlemen!

I decided to rep @phildawson because he has a lower count, hope that is okay ;-) This whole stuff is new to me so I wasn't aware of a Collection/Single Row.

Please or to participate in this conversation.