Sumit
Sumit

Reputation: 1325

Recursively display dropdown in laravel

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

Answers (3)

MaGnetas
MaGnetas

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

Jocke Med Kniven
Jocke Med Kniven

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

Francesco Malatesta
Francesco Malatesta

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

Related Questions