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.

      • public function categories(){

        return $this->hasMany(‘App\Category’, ‘parent_id’);


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

  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” => []

  14. This is awesome! Very useful! Is there a way to have a recursive routes so you may access for example, /parent/child/subchild? I know about optional routes but not sure how to have that recursive?

  15. not working for me.

    I have 5 depth comments.

    class Comment extends Model
    public function comments()
    return $this->hasMany(Comment::class, ‘parent_id’);
    function childrenComments(){

    return $this->hasMany(Comment::class, ‘parent_id’)->with(‘comments’);

    in Controller

    $comments = Comment::where(‘id’, $id)

    It just shows 3 depth only with following tree structure.

    – childrenComments
    – children

    This structure needs another looping codes

    Any better idea?

    • The same thing happened for me. It went three levels deep and stopped. Mind you, I am testing it for a rest API solution. It seems to get the infinite subcategories, you need an extra foreach loop. Not tried it yet though.

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

    I want to get count of child categories too. But this doesnot works

  17. I am trying to make a recursive view in table form using a blade view with the CRUD method. When deleting a child, the parent is also deleted. How about a solution for this

  18. Hey Povilas. Amazing tutor. I have modified it a little bit for my needs but it saves me a lot of time.
    Thank you so much.

    I also have a question. How I can pass variable to the model (to build WHERE clause. for example I need to get ‘posts’ where is_public = 1). As you can see I’m connecting the POSTS table in categories function.

    Shortly I want to get this:

    public function posts(){
    return $this->belongsToMany(Post::class, ‘post_category’, ‘category_id’, ‘post_id’)->where(‘is_public’, 1);

    And if I use this I’m getting what I expecting but it’s not dynamic (I want to show public post to all user and ALL posts to logins user for example):

    My code:

    $categories = PostCategory::whereNull(‘parent_id’)->with(‘categories’)->orderBy(‘order’)->get();


    public function posts(){
    return $this->belongsToMany(Post::class, ‘post_category’, ‘category_id’, ‘post_id’);

    public function categories(){
    return $this->hasMany(PostCategory::class, ‘parent_id’)->with(‘posts’);

    public function childrenCategories(){
    return $this->hasMany(PostCategory::class, ‘parent_id’)->with(‘categories’);

    Thank you in advance!

  19. Very Helpful but sir kindly explain me how categories.blade.php and child_categories.blade.php are working.Thanx..

  20. I have a post that has a child category of Coffee and Coffe is under the parent of Food.

    So I was able to add `Post::where(‘id’, 14)->with(‘category’, ‘category.parentCategories’)->get()`

    And added the following to my model CategoryModel.php

    public function parentCategories()
    return $this->hasMany(Category::class, ‘id’, ‘category_id’);

    Works like a charm, I only have two levels of categories but I am guessing that you could modify the above and add `->with(‘parentCategories’)`

    Thanks for the help!

  21. This article help me very much.
    But I have one more issue as I m new in laravel section. I have parent category with many sub categories and each sub categories have many services. I don’t know how to relate it theses three tables and it’s mandatory to create two tables orone table will be enough to relate all three of them

  22. can you please update it and add following code for API lovers?

    public function childrenCategoriesTree()
    return $this->categories()->with(‘childrenCategoriesTree’);

  23. hello.
    how can i print items(product1) that belong to a child category (category_id(2)) when i in a parent category page (name(category1))?
    i mean
    category table
    id(1) | name(category1) |category_id(null)
    id(2) | name(category2) |category_id(1)

    product table
    id(1) | name(product1) |category_id(2)

  24. How would this look like for recursive m:n relations? I’ve got articles in articles in articles … which i would use as a kind of tree e.g. i pick the topmost article and get all the other or articles in the relation. Additionally to that, most Articels can be used in other articles too – this is needed for the electronic devices we are constructing!
    Is there way to do that in eloquent somehow? like described above?

  25. Hello Povillas, I need to work on an accounting system, and in our country the structure of a trial balance report is similar to the example presented in this post, the difference is that you have to keep a sum, example:

    1 -> 1000.00
    11 -> 500.00
    1101 -> 500.00
    110101-> 500.00
    11010101-> 300.00
    11010102-> 200.00
    12 -> 500.00
    1201 -> 500.00
    120101 -> 500.00
    12010101 -> 500.00

    could be done with this same structure

  26. Hello Mr Korop, I`ve been reading the comments here and also checked on your YouTube channel and I haven`t found anything related to performance. This is my current issue, I`m retrieving the recursive relation like this:


    The recursive relations has a considerable amount of levels so, I get “Maximum execution time of 30 seconds exceeded”. If I remove the recursive relationship (->with(‘allChildren’)) it works fine.

    Is there a way the recursive relation can be optimized? Any advice?

    Best Regards,


Please enter your comment!
Please enter your name here