Eloquent: Recursive hasMany Relationship with Unlimited Subcategories

Quite often in e-shops you can see many level of categories and subcategories, sometimes even unlimited. This article will show you how to achieve it elegantly with Laravel Eloquent.

We will be building a mini-project to views children shop sub-categories, five level deep, like this:

Database Migration

Here’s a simple schema of DB table:

Schema::create('categories', function (Blueprint $table) {

We just have a name field, and then relationship to the table itself. So most parent category will have category_id = NULL, and every other sub-category will have its own parent_id.

Here’s our data in the database:

Eloquent Model and Relationships

First, in app/Category.php we add a simple hasMany() method, so category may have other subcategories:

class Category extends Model

    public function categories()
        return $this->hasMany(Category::class);


Now comes the biggest “trick” of the article. Did you know that you can describe recursive relationship? Like this:

public function childrenCategories()
    return $this->hasMany(Category::class)->with('categories');

So, if you call Category::with(‘categories’), it will get you one level of “children”, but Category::with(‘childrenCategories’) will give you as many levels as it could find.

Route and Controller method

Now, let’s try to show all the categories and subcategories, as in the example above.

In routes/web.php, we add this:

Route::get('categories', 'CategoryController@index');

Then, app/Http/CategoryController.php looks like this:

public function index()
    $categories = Category::whereNull('category_id')
    return view('categories', compact('categories'));

As you can see, we’re loading only parent categories, with children as relationships. Simple, huh?

View and Recursive Sub-View

Finally, to the Views structure. Here’s our resources/views/categories.blade.php:

    @foreach ($categories as $category)
        <li>{{ $category->name }}</li>
        @foreach ($category->childrenCategories as $childCategory)
            @include('child_category', ['child_category' => $childCategory])

As you can see, we load the main categories, and then load children categories with @include.

The best part is that resources/views/admin/child_category.blade.php will use recursive loading of itself. See the code:

<li>{{ $child_category->name }}</li>
@if ($child_category->categories)
        @foreach ($child_category->categories as $childCategory)
            @include('child_category', ['child_category' => $childCategory])

As you can see, inside of child_category.blade.php we have @include(‘child_category’), so the template is recursively loading children, as long as there are categories inside of the current child category.

And, that’s it! We have unlimited level of subcategories – in database, in Eloquent relationships, and in Views.

Like our articles?
Check out our Laravel online courses!


  1. I know you are using category_id so it is the convention. But for me, it is not clear. I would call it parent_id or just parent


    Thank you for the article.

    • This is fine, but it breaks out of the Laravel defaults so if you are working with a team of devs, or pass it on as legacy, then it can be harder for them to know what’s happening.

      Also ‘parent’ is parent what? Id? Model? Just the work parent makes me think it would be a Model returned, but actually it is the Id, so calling it parent_id removes all ambiguity.

  2. Though it could provide a solution to n level of hierarchical data, it will consume little more memory and response load time get slow.

    Alternate solution is, Using query builder I can give solution.
    N level of hierarchy, One function, Onetime load using boot(), One query, one time will execute at the application bootstrap level. dynamically can manage category sub-category

    thanks and regards

  3. I use such notation:

    public function children()
    return $this->hasMany(TriggerAction::class, ‘parent_id’);

    public function childrenNested()
    return $this->hasMany(TriggerAction::class, ‘parent_id’)->with(‘childrenNested’);

    This way it is visible that nested relation is called “recursively”, thus having unlimited deep.

    • there is a typo in this post, you should do `with(‘childrenCategories’)` and not `with(‘categories)`

      public function childrenCategories()
      return $this->hasMany(Category::class)->with(‘childrenCategories’);

      • thanks for sporting it, it gave me hard times when i use with the queries where too much and after rectifying it with your solution above the queries on debugbar where few what an improvement.

  4. how can I delete a category with all sub-categories?!
    i write this code but it don’t work


  5. How to now display all products for the TOYS category? (products from all children of the TOYS category) and how to count all products belonging to the TOYS children categories? *product belongs to only one category

    • You could get all the categories, then pluck all the ID’s and then select all products where they belong to a category with one of the category ID’s.

      Maybe you can even do it in the relationship definition:

      return $this->hasMany(Category::class)->with(‘categories.products’);

      But that I haven’t tried.

  6. Bad post, t sure
    public function childrenCategories()
    return $this->hasMany(Category::class)->with(‘categories’);
    public function childrenCategories()
    return $this->hasMany(Category::class);
    and you will error n +1 query

  7. How can I pass condition to the relation, If I want to sort category at level 1, it’s ok but I can’t sort for the children if the condition set by user

  8. on lvl 1 nested works ok category->subcategory but on lvl 2+ category->subcategory->subcategory shows

    View [child_category] not found. (View: /var/www/html/resources/views/admin/categories/child_category.blade.php)

  9. Hi Povilas,
    I want to use this kind of relation for user sponsorship (an user propose the app to another).
    So, my user table would have an user_id (or parent_id) and i want to do some operations on it like :
    I want all childs user but with a max nested level to 5 (like a tree), or inverse, get me the 5 directs parents from an user. Give me all the users at level 3 from an user. How many child have an user on a particular level or on his 5 levels, etc…
    Is it possible easily ?
    Thx for all your content, i follow you and watch all your daily video on youtube.
    Sry for my english.
    Bonjour de France.

    • Hi Jeremy,
      Your question “Is it possible easily?”
      Answer is “Not really”. I haven’t tried your scenario and can’t answer in a comment, I would have to set up the project and do some experiments, so that would take hours to give you the answer, sorry.

  10. This approach is not self protected from infinite loop.
    What if we have this situation?
    Id – parent_id
    1 – 2
    2 – 1

    Is there a solution within this Eloquent Model.

  11. Hi,
    I have item which has following fields id, description, value, parent_id, calculated value
    what would be your approach to have automatic categorisation and sums of items so say if item is parent to other item it will summarise the values of all children and than go up to the grand total.
    Category A 500
    item 1-CA 200
    item 2-CA 300
    item 1-1-CA 170
    item 2-1-CA 230
    Category B 200
    item 1-CB 150
    item 2-CB 50
    Total 700

    I have use case where I do budget and put provisional value for the item but then need to add details (children) and use inserted value as a validation I covered everything in the parent item value.

  12. The article was very useful, Mr. Povilas! Thanks!
    just one question:
    how can I create indentation for sub category?
    something like this:

  13. Hey, @PovilasHow i do this use with Laravel Resource?

    MachineResource Example Output:
    “id” => 1,
    “title” => “Machine”,
    “parent_id” => null,
    “childs” => [
    “id” => 10,
    “title” => “Car”,
    “parent_id” => 1,
    “childs” => [
    “id” => 20,
    “title” => “Volvo”,
    “parent_id” => 10,
    “childs” => []


Please enter your comment!
Please enter your name here