How to Inverse the Eloquent Has One and Has Many Through (laravel 5.8)?

Viewed 3428

I have three relational table attached below.

https://drive.google.com/file/d/1q1kdURIwFXxHb2MgdRyBkE1e3DMug7r-/view?usp=sharing

I have also three separate models where defined relation among all of my table's.I can read the City Model's information from Country model using hasManyThrough() relation But cannot read the Country information from City model. I have tried to retrieve City model's using ``hasManyThrough``` but didn't get result (attached as commented country method ). Please read my model and it's relational method here..

Is there someone to help me for getting City model's information using Eloquent method hasManyThrough / hasManyThrough or using inverse of hasManyThrough / hasManyThrough ?

01.

<?php

namespace App\Hrm;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Country extends Model
{
    //use SoftDeletes;
    protected $fillable = ['name','description','status'];


    public function districts(){
        return $this->hasMany(District::class);
    }


    public function cities(){
        return $this->hasManyThrough(City::class,District::class);
    }


}

02.

<?php

namespace App\Hrm;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class District extends Model
{
    //use SoftDeletes;
    protected $fillable = ['country_id','name','description','status'];


    public function country(){
        return $this->belongsTo(Country::class);
    }

    public function cities(){
        return $this->hasMany(City::class);
    }

}

3.

namespace App\Hrm;

use App\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class City extends Model
{
    //use SoftDeletes;
    protected $fillable = ['district_id','name','description','status'];

    public function district(){
        return $this->belongsTo(District::class);
    }

//    public function country(){
//        return $this->hasOneThrough(Country::class, District::class);
//    }
3 Answers

Doesn't look like there is a native way to define the inverse of a "hasManyThrough" relationship yet in Laravel. There have been a few issues opened on github to request it, but they were closed.

You could use the staudenmeir/belongs-to-through package if you don't mind installing a third-party package for this functionality. Then you should be able to define a belongsToThrough relationship like this:

class City extends Model
{
    use \Znck\Eloquent\Traits\BelongsToThrough;

    public function country() {
        return $this->belongsToThrough(Country::class, District::class);
    }
}

Why can't use parent method?

$city = City::find(1);
$country = $city->district->country();

i just had a similar situation i was able to accomplish a belongsToThrough with hasOneThrough

public function country()
{
    return $this->hasOneThrough(
        Country::class,       // model we are trying to get
        District::class,      // model we have an _id to
        'id',                 // WHERE `district`.`id` = `city`.`district_id`
        'id',                 // `countries`.`id`
        'district_id',        // local column relation to our through class
        'country_id'          // `district`.`country_id`
    );
}

what this should generate is

SELECT * FROM `countries` 
    INNER JOIN `districts` 
        ON `districts`.`country_id` = `countries`.`id`
    WHERE `districts`.`id` = ?


-- ? == city.district_id

Database structure:

City:
    id: increments
    district_id: integer
    ...

Country:
    id: increments
    ...

District:
    id: increments
    country_id: integer
    ...

we can then do $city->country

note: i have not fully tested this but with the testing that i have done it 'works'

Edit: i originally thought that i needed to leave the localKey parameter null otherwise the relation wont work. it turns out i didnt fully understand what that column was doing and that was wrong. That key is the local column that relates to our through column (unless i still have more to learn/figure out), when left the value as null, it would use the local id column which a. is the wrong value, b. can also be out of range (which is how i discovered it was using the wrong value)

in my testing i only had two rows, both with the same relations. what i didnt realize though was that on the "through table" both row 1 and 2 and the same related (relation where are trying to reach) so i didnt notice the issue right away. hopefully now its all working

Related