Reputation: 1325
suppose I have table named categories
such as:
id parent_id title
1 0 food
2 1 drinks
3 2 juice
4 0 furniture
5 3 tables
now I want to create dropdown menu on laravel such that it recursively displays child category under parent category with proper indentation or -
mark as per depth.E.g.:
<select>
<option value="1">food</option>
<option value="2">-drinks</option>
<option value="3">--juice</option>
<option value="4">furniture</option>
<option value="5">-tables</option>
</select>
Above one is static but I want to generate dropdown structure dynamically as like above recursively for any depth of child category from categories
table in laravel.
Upvotes: 2
Views: 3758
Reputation: 5108
Run get
method on your Category
Eloquent model or use query builder to get all the categories.
Then write a function and call it recursively as many times as you need. filter method would be really helpful to work with your categories collection
Something like this should work:
function getCategories($categories, &$result, $parent_id = 0, $depth = 0)
{
//filter only categories under current "parent"
$cats = $categories->filter(function ($item) use ($parent_id) {
return $item->parent_id == $parent_id;
});
//loop through them
foreach ($cats as $cat)
{
//add category. Don't forget the dashes in front. Use ID as index
$result[$cat->id] = str_repeat('-', $depth) . $cat->title;
//go deeper - let's look for "children" of current category
getCategories($categories, $result, $cat->id, $depth + 1);
}
}
//get categories data. In this case it's eloquent.
$categories = Category::get();
//if you don't have the eloquent model you can use DB query builder:
//$categories = DB::table('categories')->select('id', 'parent_id', 'title')->get();
//prepare an empty array for $id => $formattedVal storing
$result = [];
//start by root categories
getCategories($categories, $result);
Didn't test it myself, but the idea should be clear enough. The good thing is you're only executing a single query. The bad thing is you load the whole table into memory at once.
If your table has more columns that you don't need for this algorithm you should specify only the needed ones in your query.
Upvotes: 2
Reputation: 4049
Not the most elegant, but gets the job done:
<?php
$data = [
['id' => 1, 'parent_id' => 0, 'title' => 'food'],
['id' => 2, 'parent_id' => 1, 'title' => 'drinks'],
['id' => 3, 'parent_id' => 2, 'title' => 'juice'],
['id' => 4, 'parent_id' => 0, 'title' => 'furniture'],
['id' => 5, 'parent_id' => 4, 'title' => 'tables']
];
function recursiveElements($data) {
$elements = [];
$tree = [];
foreach ($data as &$element) {
$element['children'] = [];
$id = $element['id'];
$parent_id = $element['parent_id'];
$elements[$id] =& $element;
if (isset($elements[$parent_id])) { $elements[$parent_id]['children'][] =& $element; }
else { $tree[] =& $element; }
}
return $tree;
}
function flattenDown($data, $index=0) {
$elements = [];
foreach($data as $element) {
$elements[] = str_repeat('-', $index) . $element['title'];
if(!empty($element['children'])) $elements = array_merge($elements, flattenDown($element['children'], $index+1));
}
return $elements;
}
$recursiveArray = recursiveElements($data);
$flatten = flattenDown($recursiveArray);
print_r($flatten);
/*
Outputs:
Array
(
[0] => food
[1] => -drinks
[2] => --juice
[3] => furniture
[4] => -tables
)
*/
Upvotes: 2
Reputation: 679
First of all, you could define a getCategories method on your controller. A recursive method. Ideally, you should implement something like this:
...
// utility method to build the categories tree
private function getCategories($parentId = 0)
{
$categories = [];
foreach(Category::where('parent_id', 0)->get() as $category)
{
$categories = [
'item' => $category,
'children' => $this->getCategories($category->id)
];
}
return $categories;
}
...
Right after, you should pass the final array/collection (or whatever you choose) to the view.
return view('my_view', ['categories' => $this->getCategories()])
Finally, you could use a solution similar to this one.
Upvotes: 2