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

princeoo7's avatar

Nested Eloquent Relationship on three or more tables.

Hello everyone.

i have a situation where i want to nest the records fetched by eloquent relationship from model itself.

Tables are :

1) Countries
2) States
3) Cities

requirement is that when fetching data via model, countires > states > cities is required in nested array. but currently i am only able to get countries > states with hasMany relationship.

models code is as followed:

//Countries.php

<?php

    namespace App;

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

    class Countries extends Model
    {
            use SoftDeletes;

            public function states()
            {
                return $this->hasMany(States::class, 'country_id', 'id')->where('status', 1); 
            }

            // public function cities()
            // {
            //     return $this->hasManyThrough(Cities::class, States::class, 'country_id', 'state_id', 'id', 'id');
            // }
    }

//States.php
<?php

    namespace App;

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

    class States extends Model
    {
         use SoftDeletes;

            public function cities()
            {
                return $this->hasMany(Cities::class, 'state_id', 'id'); 
            }

            public function country()
            {
             return $this->belongsTo(Countries::class, 'country_id', 'id');
            }
    }

//States.php
<?php

    namespace App;

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

    class Cities extends Model
    {
        use SoftDeletes;

            public function state()
            {
             return $this->belongsTo(States::Class, 'state_id', 'id');
            }

            public function country()
            {
                return $this->belongsTo(Countries::Class, 'country_id', 'id');
            }
    }

Countries controller function :

public function index(Request $request)
{
    if($request->isMethod('get') && isset($_GET['cid']) ) {
        $records = Countries::where('id', $_GET['cid'])->get();
    } else {
        $records = Countries::get();
    }
    
    $records->map(function ($data) {
        return $data->states;
    });
    // $records->map(function ($data) {
    //     return $data->states->cities;
    // });
    $data  = $records;
    // return print_r($data);
    return Response()
            ->json([
                'collection' => $data
            ]);
    return view('pages.countries.index', ['records' => $records]);
}

0 likes
7 replies
princeoo7's avatar

array:1 [▼
0 => array:12 [▼
    "id" => 103
    "name" => "India"
    "alpha-2-code" => "IN"
    "alpha-3-code" => ""
    "un-code" => ""
    "status" => 1
    "creator" => 1
    "updated_by" => 1
    "created_at" => null
    "updated_at" => null
    "deleted_at" => null
    "states" => array:41 [▼
        0 => array:10 [▼
        "id" => 1
        "name" => "Andaman and Nicobar Islands"
        "country_id" => 103
        "status" => 1
        "creator" => 1
        "updated_by" => 1
        "created_at" => "2019-02-26 08:49:39"
        "updated_at" => "2019-02-26 08:49:39"
        "deleted_at" => null
        "cities" => array:4 [▶]
    ]
    1 => array:10 [▶]
    2 => array:10 [▶]
    3 => array:10 [▶]
    4 => array:10 [▶]
    5 => array:10 [▶]
    6 => array:10 [▶]
    7 => array:10 [▶]
    8 => array:10 [▶]
    9 => array:10 [▶]
    10 => array:10 [▶]
    11 => array:10 [▶]
    12 => array:10 [▶]
    13 => array:10 [▶]
    14 => array:10 [▶]
    15 => array:10 [▶]
    16 => array:10 [▶]
    17 => array:10 [▶]
    18 => array:10 [▶]
    19 => array:10 [▶]
    20 => array:10 [▶]
    21 => array:10 [▶]
    22 => array:10 [▶]
    23 => array:10 [▶]
    24 => array:10 [▶]
    25 => array:10 [▶]
    26 => array:10 [▶]
    27 => array:10 [▶]
    28 => array:10 [▶]
    29 => array:10 [▶]
    30 => array:10 [▶]
    31 => array:10 [▶]
    32 => array:10 [▶]
    33 => array:10 [▶]
    34 => array:10 [▶]
    35 => array:10 [▶]
    36 => array:10 [▶]
    37 => array:10 [▶]
    38 => array:10 [▶]
    39 => array:10 [▶]
    40 => array:10 [▶]
    ]
 ]

]

This is what i got. now when i am doing $records->states, it gives me

Countries [states] does not exist on this collection instance. (View: C:\xampp\htdocs\laravel\resources\views\pages\countries\index.blade.php)

@tykus @grenadecx any thoughts on the same ?

grenadecx's avatar

@PRINCEOO7 - You're almost there. Remember that records is an array as well since you call it with ->get()

So for example:

 $records[0]->states // This will work on the array above

So you need to loop records as well and or fetch one country if that's what you want.

2 likes
princeoo7's avatar

@GRENADECX - this worked :) but my question now is the why am i able to use $records in foreach loop and not $records->states.

//index.blade.php

<select name="countries" id="">
@foreach($records as $key => $value)
    <option value="{{ $value->id }}">{{ $value->name }}</option>
@endforeach 
</select>


<select name="countries" id="">
@foreach($records[0]->states as $key => $value)
    <option value="{{ $value->id }}">{{ $value->name }}</option>
@endforeach 
<select>

grenadecx's avatar

Because they are all arrays. If you need to loop everything to get into the cities, you would need to nest the foreach.

For example:

// Loop all countries
@foreach($records as $key => $country)

    // Loop all states inside single country
    @foreach($country->states as $key2 => $state)

        // Loop all cities inside single state
        @foreach($state->cities as $key3 => $city)
            // Now you reach city
        @endforeach 

    @endforeach

@endforeach 
1 like

Please or to participate in this conversation.