Nested data is a pain point for a lot of developers. Relationships, eager loading and recursively iterating and displaying hierarchical data is enough to cause a headache.
In this post, I'm going to show you the easiest, cleanest (and most importantly, fastest) way to deal with nested data like this – and we're using the example of *categories, *much like you'd find in the navigation of an e-commerce store.
Setting up categories
First up, create a fresh Laravel project so we can play around. Then add a route that renders a plain view. This is where we'll output our recursive category list.
The parent_id here is what links each category to a parent. If the parent_id is null, it's a *root level *category. Otherwise, the parent_id will reference the parent. We can create an unlimited amount of nesting levels this way.
Run your migrations.
Now let's create some example categories. You can do this manually in your database client, or use the factory that was generated. Let's use the factory here.
In CategoryFactory, fill in the definition to include the title and slug.
Your database should look a little something like this now.
To test this, we really just need a couple more child categories for one of the child categories we already have, so create two more categories for (in this case) the category with an ID of 3.
You should now have a set of data similar to this.
Here's how this would look as a hierarchy, and how we would want to see it in the browser.
|- Et quisquam consequatur enim.|-- Aut itaque voluptas temporibus repellendus.|--- Aliquam dolorum qui molestiae dignissimos.|--- Natus aut numquam aliquam iusto dolor.|- Et dolor quis dolorem dolore.|-- Sunt asperiores est qui.
Of course, you could keep going with the nest, but we'll leave it at this for now.
You might have worked with or heard the term 'nested sets' before, but we're taking a different approach.
Adjacency lists provide recursive relationships using common table expressions (CTE). A CTE allows you to define a temporary set of data in the execution of an SQL statement. Essentially we can, using SQL, grab all of the nested data we need in one query, very quickly, and build it back up to be displayed as a tree. You can read more about this with a quick Google search.
If this sounds daunting, don't worry. Luckily for us, there's a brilliant package that handles all of this for us in Laravel specifically.
Usually with relationships like this, we need to consider eager loading to avoid n+1 problems. It's also impossible to eager load a potentially unlimited amount of children of a model, because we'd need to always know how many levels of hierarchy there were.
As you can see, we have exactly one query to deal with all of this. The package we're using harnesses CTE and gives us back a collection of every item in the hierarchy. Even if you added thousands of children, you'd still only have one query.
There's a load more you can do with this package depending on your needs. But to be honest, it's worth pulling in just for this tree building functionality alone.
Often a massive pain point in development, we've solved a potentially huge amount of category (or other model) nesting with very little code.