Why can't Laravel/Eloquent use JOIN for Eager Loading?

empz picture empz · May 28, 2014 · Viewed 7.9k times · Source
<?php

class Cat extends Eloquent {

    public function user() {
        return $this->belongsTo('User');
    }
}

class User extends Eloquent {

    public function cats() {
        return $this->hasMany('Cat');
    }
}

Now:

$cats = Cat::with('user')->get();

Performs 2 queries:

select * from `cats`
select * from `users` where `users`.`id` in ('1', '2', 'x')

Why can't it just do:

select * from cats inner join users on cats.user_id = users.id

For those saying that there are both id columns in the table, that could be easily avoided with aliases:

select 
    c.id as cats__id,
    c.name as cats__name,
    c.user_id as cats__user_id,
    b.id as users__id,
    b.name as users__name
from cats c
inner join users b on b.id = c.user_id

UPDATE

Someone pointed out that Eloquent doens't know the columns of the tables from the models, but I guess they could provide a way to define them in the model so then it could use aliases and do a proper join instead of an extra query.

Answer

DBCrocky picture DBCrocky · Jun 10, 2014

My guess is that this allows for eager loading multiple one to many relationships. Say, for instance, we also had a dogs table:

class User extends Eloquent {

    public function cats() {
        return $this->hasMany('Cat');
    }

    public function dogs() {
        return $this->hasMany('Dog');
    }
}

Now we want to eager load them both with the User:

$users = User::with('cats','dogs')->get();

There is no join that would work to combine these into a single query. However, doing a seperate query for each "with" element does work:

select * from `users`
select * from `cats` where `user`.`id` in ('1', '2', 'x')
select * from `dogs` where `user`.`id` in ('1', '2', 'x') 

So, while this methodology may produce an extra query in some simple circumstances, it provides the ability to eager load more complex data where the join method will fail.

This is my guess as to why it is this way.