Austin Kregel
Austin Kregel

Reputation: 755

Retrieving all children and grandchildren of Laravel models

I have several models, all of which need the one before it to be accessed. The examples below describe a similar situation

<?php namespace backend\Models;

use Illuminate\Database\Eloquent\Model;

class Geo_Locations extends Model {
   protected $table = 'geo_locations';
   protected $fillable = ['id', 'name', 'population', 'square_miles', 'comments'];
   public function state(){
    return $this->hasMany('backend\Models\Geo_State', 'id', 'state_id');
  }
}

The "Geo_State" model

<?php namespace backend\Models;

use Illuminate\Database\Eloquent\Model;

class Geo_State extends Model {
   protected $table = 'geo_states';
   protected $fillable = ['id', 'name', 'political_obligation', 'comments'];
   public function city(){
    return $this->hasMany('backend\Models\Geo_City', 'id', 'city_id');
  }
}

And then the "Geo_City" model would be

<?php namespace backend\Models;

use Illuminate\Database\Eloquent\Model;

class Geo_City extends Model {
   protected $table = 'geo_cities';
   protected $fillable = ['id', 'name', 'population', 'comments'];
   public function miles(){
    return $this->hasMany('backend\Models\Geo_Mile', 'id', 'mile_marker');
  }
}

and it can continues but for the sake of ease, I'll stop there. What I would like to accomplish, and I'm sure many others would as well. Would be a way to retrieve all data related to one of the main models. For example, I want to get all the cities and their respective states within a location. What I would like for a result is something like

print_r(json_encode($a->getChildren(), JSON_PRETTY_PRINT));

should print out

{
   "Geo_Location":{
        "name":"United States",
        "population":"300 Some Million",
        "comments":"they have a lot of issues",
        "Geo_State":{       
           "name":"Texas",
           "population":"Some big number",
           "comments":"they have a lot of republicans",       
           "Geo_City":{
              "name":"Dallas",
              "population":"A lot",
              "comments":"Their food is awesome"
           }
        }
   }

}

I have some of the code in a gist, while it may be poorly documented it's all I've been working with and it's my third attempt at doing this.

Individual calls to the route /api/v1/locations/1 would return all the info for United States but will leave out the Geo_State and below. A call to /api/v1/state/1 will return Texas's information but will leave out the Geo_Location and the Geo_City.

The issue in what I have is that it's not recursive. If I go to the route /api/v1/all it's suppose to return a json array similar to the desired one above, but it's not getting the children.

Upvotes: 3

Views: 5714

Answers (2)

Anoua
Anoua

Reputation: 121

It sounds like what you want is to load the relationships the model has when you call that model. There are two ways I know you can do that in laravel using eager loading.

First way is when you retrieve the model using the with parameter, you can add the relationship and nested relationships like below.

$geoLocationCollectionObject = 
                      Geo_Location::with('state', 'state.city', 'state.city.miles', 'etc..')
                     ->where('id', $id)
                     ->get();

This will return a collection object - which has a function to return the json (toJson). I'm not sure how it handles the relationships (what your calling children) so you may need to make a custom function to parse the collection object and format it how you've specified.
toJson Function

Second option is similar to the first but instead you add the with parameter (as protected $with array) to the model so that the relationships are loaded everytime you call that model.

Geo_Location class
    protected $with = array('state');
Geo_State class
    protected $with = array('city');
Geo_City class
    protected $with = array('miles');
etc...

Now when you retrieve a Geo_Location collection object (or any of the others) the relationship will already be loaded into the object, I believe it should be recursive so that the city miles will also be called when you get a state model for instance - but haven't tested this myself so you may want to verify that. I use ChromeLogger to output to the console and check this myself.

If you do end up needing a custom json_encode function you can access the relationship by doing $geoLocation->state to retreive all states and then for each state $state->city etc... See the docs for more info. Laravel Docs on Eager Loading

I wasn't really sure what was going on in the code you linked, but hopefully this helps some.

Upvotes: 3

Austin Kregel
Austin Kregel

Reputation: 755

So after a bit of fiddling around, turns out that what I am looking for would be

Geo_Locations::with('state.city')->get();

Where Geo_Locations is the base model. The "state" and the "city" would be the relations to the base model. If I were to return the query above, I would end up getting an json array of what I am looking for. Thank you for your help Anoua

(To make this more dynamic I'm going to try to find a way to get all of the names of the functions to automate it all to keep on track with what my goal is. )

Upvotes: 1

Related Questions