What is the best way to retrieve/gather (eager load) relations of parents?

Viewed 43

I have a simple nested category system with features. So each category has many features and vice versa.

categories

id   title   parent_id
--   -----   ---------
1    car     0
2    suv     1
3    sport   1
4    4x4     2
5    4x2     2

features

id   title
--   -----
1    color
2    dimension
3    ps

category_feature

id  category_id  feature_id
--  -----------  ----------
1   1            1
2   1            2
3   2            3

category

public function children()
{
    return $this->hasMany($this, 'parent_id', 'id')->with('children');
}

public function features()
{
    return $this->belongsToMany(Feature::class, 'category_feature', 'category_id', 'feature_id');
}

feature

public function categories()
{
    return $this->belongsToMany(Category::class, 'category_feature', 'feature_id', 'category_id');
}

for example:

             car
              |
        suv-------sport
         |
    4x4-----4x2

Here the car category is the root and has for example "color" and "dimension" features and the suv category has "ps" feature. Of course these features have variants but that is not the point here.

What I want is to (eager load) retreive all the ancestor features (cascade) when I call the "4x2" category. What would the best way to achive this?

1 Answers

After some tries I realized the solution was already here. In an easy way I just needed to add a recurring parents relationship as I was already using on children relationship. After that I was able to eager load all the existing parents (including features relationship) along to the root. And I loved it. Easy, clean and fast.

Here is what I mean:

public function children()
{
    return $this->hasMany($this, 'parent_id', 'id')->with('children');
}

I already was using this relations above which makes it possible to load all children below the selected category. It is recurring from itself because we use the eager loader with('children') at the end.

So I just used the same way to get the parents like:

public function parents()
{
    return $this->hasMany($this, 'parent_id', 'id')->with('parents.features');
}

Now I will be able to eager load the parent till the root. If it is not top level category there will be always a single parent until it reaches the top. And not to forget I have to eager load features for all parents as well if I want to collect all features till top. For this I just add features with dot like with('parents.features');. After this point I of couse need to make some extra process to gather all the features but that is easy as well.

Besides if the model is very crowded I could even sparse some columns by excluding select only the required ones. Funny easy.

Related