Laravel hasManyThrough equivalent: belongsTo relationship through another model

10,593

Solution 1

public function target() { 
    $middle = $this->belongsTo('Middle', 'middle_id'); 
    return $middle->getResults()->belongsTo('Target'); 
}

Update:

Starting from laravel 5.8 you can use the hasOneThrough relationship:

public function target() { 
    return $this->hasOneThrough('Target', 'Middle');
}

Solution 2

If this is situation like message in a bottle, and bottle is owned by the user (user > bottle > message)

The only way I know to get the relation object is:

// THIS IS IN App\Message

public function bottle()
{
    return $this->belongsTo('App\Bottle');
}

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

Solution 3

You can use hasOneThrough but you need to customize keys.

public function parent()
{
    return $this->hasOneThrough(Parent::class, Middle::class, 'id', 'id', 'middle_id', 'parent_id');
}

Origin belongs to Middle, and Middle belongs to Parent. Middle need has parent_id foreign key, and Origin has middle_id foreign key.

Finally you can use:

Origin::find(1)->parent;
Share:
10,593

Related videos on Youtube

BenjaminRH
Author by

BenjaminRH

I am a professional web developer. I make web apps with node.js (Meteor, Express) and Laravel, and code in Go and Python. My editor is Vim.

Updated on June 03, 2022

Comments

  • BenjaminRH
    BenjaminRH almost 2 years

    I've got a model, it belongs to another model, that model belongs to a third model, and I want an eloquent method to relate the first model to the third one.

    There doesn't appear to be a belongsToThrough (or hasOneThrough) method, though. I've already tried chaining multiple belongsTo methods, but that hasn't worked (Call to undefined method Illuminate\Database\Query\Builder::belongsTo()). Any ideas?

    Here is an example of the models:

    // The first model
    // Schema: this model has a middle_id column in the database
    class Origin extends Eloquent {
        public function middle()
        {
            return $this->belongsTo('Middle');
        }
    }
    
    // The second model
    // Schema: this model has a target_id column in the database, but NOT an origin_id column
    class Middle extends Eloquent {
        public function target()
        {
            return $this->belongsTo('Target');
        }
    }
    
    // The third model
    class Target extends Eloquent {
    }
    

    What I'd like to do is add something like the following method to the Origin model:

    // A relationship method on the first "origin" model
    public function target()
    {
        // First argument is the target model, second argument is the middle "through" model, third argument is the database column in middle model that it uses to find the target model, or soemthing
        return $this->hasOneThrough('Target', 'Middle', 'target_id');
    }
    

    So that I can use $originInstance->target->title, etc.

    • Jarek Tkaczyk
      Jarek Tkaczyk about 10 years
      have you tried hasManyThrough?
    • Jarek Tkaczyk
      Jarek Tkaczyk about 10 years
      HasOne and HasMany both extend abstract HasOneOrMany and they are pretty similar. I'm pretty sure it will work for you, however it may return a Collection instead of a single model, that could be the only downsie I can think of now.
    • Jarek Tkaczyk
      Jarek Tkaczyk about 10 years
      OK, then that's not going to work. There is no method for through relations this way currently, only the other way around A -> hasOneOrMany -> B -> hasOneOrMany C. but you can still use dot nested relations like origin->middle->target->title (if it's hasOne everywhere)
  • BenjaminRH
    BenjaminRH about 10 years
    Thanks, I appreciate the answer! I probably haven't phrased my question clearly enough, but my problem is that I need to access the target directly as a relation to the origin. As it is setup currently, I could use $origin->middle->target to access the target, because each one belongs to the next. However, that won't work for me, as I need to skip the middle-man. I hope that clears my problem up a bit.
  • Yuri Scarbaci
    Yuri Scarbaci about 7 years
    I would like to know more about "getResults()", i am reading this answear 2 years later than it was posted in, and by a simple google search it looks like getResults() got replaced by get(). Still need to confirm this, but i hope it could help others looking at this in 2017!
  • Razor
    Razor about 7 years
    AFAIK this answer is still valid, I double checked each Relation class and getResults() wasn't removed (e.g. HasMany Relation L5.4), did you test the answer?
  • Jasmeet Singh
    Jasmeet Singh over 6 years
    It works. But is it efficient? Will this create a separate query for each bottle of each message, instead of running just one query for the entire request of a user.
  • Mladen Janjetovic
    Mladen Janjetovic over 6 years
    @JasmeetSingh, Of course it will create separate queries, but it makes life easier while coding. Having in mind that object caching is standard thing in apps these days, I don't mind. If you don't use such things you will just have to dance between ease of coding and app speed. One query for the entire request of a user with joins will be much faster, but it is not much reusable further in app. And we want to separate database layer from app so no raw SQL queries in code, neither
  • Mladen Janjetovic
    Mladen Janjetovic over 6 years
    @JasmeetSingh or you can just use Eager Loading
  • Charles Wood
    Charles Wood over 3 years
    Unfortunately this doesn't support eager loading :(
  • Charles Wood
    Charles Wood over 3 years
    Oh man! I know that "thanks" comments are discouraged, but thank you so much! I couldn't believe this worked after all the other convoluted things I tried!
  • Razor
    Razor over 3 years
    @CharlesWood Laravel added a function hasOneThrough that supports eager loading, I've updated my old answer, thanks.
  • Marek Gralikowski
    Marek Gralikowski over 3 years
    Happy to help ;)
  • Charles Wood
    Charles Wood over 3 years
    @Razor Nice! Bear in mind that to simulate belongsToThrough, you may need to customize the keys. See stackoverflow.com/a/58295018/1110820. (This is the solution I ended up using.)