Create nested list from PHP array for dropdown select field

13,398

Solution 1

Try this;

function buildTree(Array $data, $parent = 0) {
    $tree = array();
    foreach ($data as $d) {
        if ($d['parent'] == $parent) {
            $children = buildTree($data, $d['id']);
            // set a trivial key
            if (!empty($children)) {
                $d['_children'] = $children;
            }
            $tree[] = $d;
        }
    }
    return $tree;
}


$rows = array(
    array ('id' => 1, 'name' => 'Test 1', 'parent' => 0),
    array ('id' => 2, 'name' => 'Test 1.1', 'parent' => 1),
    array ('id' => 3, 'name' => 'Test 1.2', 'parent' => 1),
    array ('id' => 4, 'name' => 'Test 1.2.1', 'parent' => 3),
    array ('id' => 5, 'name' => 'Test 1.2.2', 'parent' => 3),
    array ('id' => 6, 'name' => 'Test 1.2.2.1', 'parent' => 5),
    array ('id' => 7, 'name' => 'Test 2', 'parent' => 0),
    array ('id' => 8, 'name' => 'Test 2.1', 'parent' => 7),
);

$tree = buildTree($rows);
// print_r($tree);

function printTree($tree, $r = 0, $p = null) {
    foreach ($tree as $i => $t) {
        $dash = ($t['parent'] == 0) ? '' : str_repeat('-', $r) .' ';
        printf("\t<option value='%d'>%s%s</option>\n", $t['id'], $dash, $t['name']);
        if ($t['parent'] == $p) {
            // reset $r
            $r = 0;
        }
        if (isset($t['_children'])) {
            printTree($t['_children'], ++$r, $t['parent']);
        }
    }
}


print("<select>\n");
printTree($tree);
print("</select>");

Output;

<select>
    <option value='1'>Test 1</option>
    <option value='2'>- Test 1.1</option>
    <option value='3'>- Test 1.2</option>
    <option value='4'>-- Test 1.2.1</option>
    <option value='5'>-- Test 1.2.2</option>
    <option value='6'>--- Test 1.2.2.1</option>
    <option value='7'>Test 2</option>
    <option value='8'>- Test 2.1</option>
</select>

And in your case;

<select>
    <option value='1'>Baden-Württemberg</option>
    <option value='2'>- DMP-Verträge</option>
    <option value='50'>- Sprechstundenbedarf</option>
    <option value='52'>- Richtgrößen</option>
    <option value='53'>- Prüfungen</option>
    <option value='54'>- DMP-Verträge</option>
    <option value='55'>- Sonstige Verträge</option>
    <option value='3'>Berlin</option>
    <option value='62'>- DMP-Verträge</option>
    <option value='63'>- Prüfungen</option>
    <option value='64'>- Richtgrößen</option>
    <option value='65'>- Sonstige Verträge</option>
    <option value='66'>- Sprechstundenbedarf</option>
    <option value='4'>Brandenburg</option>
    <option value='67'>- DMP-Verträge</option>
    <option value='68'>- Prüfungen</option>
    <option value='69'>- Richtgrößen</option>
    <option value='70'>- Sonstige Verträge</option>
    <option value='71'>- Sprechstundenbedarf</option>
    <option value='5'>Bremen</option>
    <option value='72'>- DMP-Verträge</option>
    <option value='73'>- Prüfungen</option>
    <option value='74'>- Richtgrößen</option>
    <option value='75'>- Sonstige Verträge</option>
    <option value='76'>- Sprechstundenbedarf</option>
    <option value='7'>Hessen</option>
    <option value='6'>Hamburg</option>
    <option value='8'>Mecklenburg-Vorpommern</option>
    <option value='9'>Niedersachsen</option>
    <option value='10'>Nordrhein</option>
    <option value='11'>Rheinland-Pfalz</option>
    <option value='12'>Saarland</option>
    <option value='13'>Sachsen</option>
    <option value='14'>Sachsen-Anhalt</option>
    <option value='15'>Schleswig-Holstein</option>
    <option value='16'>Thüringen</option>
    <option value='17'>Westfalen-Lippe</option>
    <option value='51'>Richtgrössen</option>
    <option value='56'>Bayern</option>
    <option value='57'>- DMP-Verträge</option>
    <option value='58'>- Prüfungen</option>
    <option value='59'>- Richtgrößen</option>
    <option value='60'>- Sonstige Verträge</option>
    <option value='61'>- Sprechstundenbedarf</option>
</select>

Solution 2

this just litle change of the original code that answered by Qeremy

function printTree($tree, $r = 0, $p = null) {
    foreach ($tree as $i => $t) {
        $dash = ($t['parent'] == 0) ? '' : str_repeat('-', $r) .' ';
        printf("\t<option value='%d'>%s%s</option>\n", $t['id'], $dash, $t['name']);
        if (isset($t['_children'])) {
            printTree($t['_children'], $r+1, $t['parent']); 
        }
    }
}

Now the dashed name works great. *sorry for my english

Share:
13,398
suntrop
Author by

suntrop

Updated on July 22, 2022

Comments

  • suntrop
    suntrop almost 2 years

    I am struggling with an array I want to turn into a nested < select >

    I need:

    <select>
    <option value="1">Top1</option>
    <option value="2">Top2</option>
    <option value="9">Top3</option>
    <option value="7"> - - Top3.1</option>
    <option value="5"> - - Top3.2</option>
    <option value="12">- - - - Top3.2.1</option>
    <option value="6">Top4</option>
    <option value="4">Top5</option>
    <option value="8"> - - Top5.1</option>
    <option value="3"> - - Top5.2</option>
    

    I can't work with optgroup, because everything is selectable. As far as I know, you can't select optgroup labels.

    My array looks like this:

    [44] => Array
        (
            [id] => 1
            [name] => Test
            [slug] => test
            [parent] => 0
        )
    
    [45] => Array
        (
            [id] => 2
            [name] => Test-Sub
            [slug] => test-sub
            [parent] => 1
        )
    
    [46] => Array
        (
            [id] => 3
            [name] => Test-Sub-Sub
            [slug] => test-sub-sub
            [parent] => 2
        )
    

    I am feeling like I have tried dozens of variantions, but I can't build my form select right.

    That was my last try:

    function toDropdown($arr)
        {
            foreach ($arr as $row) {
                $cat[$row['id']] = $row['name'];
                if ($row['parent'] != 0) {
                    $cat[$row['id']] = '--' . $row['name'];
                }
            }
            return $cat;
        }
    

    But this way, it is ordered by the ID and the nesting loses its meaning.

    I'll try to go on, but if someone can help I appreciate any help!


    EDIT: PHP Data


    My function to get all categories from the DB:

    function get_categories($parent = 'all')
    {
        $this->db->select('categories.id, categories.name, categories.slug, categories.parent');
        $this->db->from('categories');
    
        if ($query = $this->db->get())
        {
            return $query->result_array();
        }
    
        return FALSE;
    }
    

    My view.php, where I output all data:

    $query = $this->datei_model->get_categories('all');
    
    foreach ($query as $row)
    {
        $parents[] = $row;
    }
    
    $tree = buildTree($parents);
    
    print("<select>\n");
    printTree($tree);
    print("</select>");
    
  • suntrop
    suntrop about 11 years
    Wow, thanks!! The code prints my select list quite well. But with every level deeper there is a dash more. <option value='1'> First </option> <option value='2'>- Example </option> <option value='50'>- Example </option> <option value='3'> Example </option> <option value='62'>-- Example </option> <option value='63'>-- Example </option> (…) <option value='56'> Example </option> <option value='60'>----- Example </option> <option value='79'> Example </option> <option value='77'>------ Example </option> <option value='78'>------- Example </option> (Sorry, can't format the code here)
  • suntrop
    suntrop about 11 years
    Yes, your're right. But, for example, the last sub level hast not one dash, but 7 dashes, although it is only on the second level. project-point.de/sub-levels.png
  • K-Gun
    K-Gun about 11 years
    It's related with your $rows structure, see $rows in answer and check differences, or show your $rows too (but real row data, not print_r'ed).
  • suntrop
    suntrop about 11 years
    I think both are structured the same way, don't they? Array ( [0] => Array ( [id] => 1 [name] => Baden-Württemberg [slug] => baden-wuerttemberg [parent] => 0 ) [40] => Array ( [id] => 73 [name] => Prüfungen [slug] => pruefungen [parent] => 5 ) )
  • K-Gun
    K-Gun about 11 years
    Bro, just show me all data like prev image? Add it to your question by clicking edit button, BUT REAL PHP DATA, NOT PRINT_R OUTPUT PLS! Verstehen?
  • K-Gun
    K-Gun about 11 years
  • suntrop
    suntrop about 11 years
    Sorry, did't know how to post my code. I put the source code at the end in my question now. That's what you meant?
  • Prabhagaran
    Prabhagaran about 7 years
    To solve the dash(-) issue, put p=0 instead of null. function printTree($tree, $r = 0, $p = 0) { foreach ($tree as $i => $t) { $dash = ($t['parent'] == 0) ? '' : str_repeat('-', $r) .' '; printf("\t<option value='%d'>%s%s</option>\n", $t['id'], $dash, $t['name']); if ($t['parent'] == $p) { // reset $r $r = 0; } if (isset($t['_children'])) { printTree($t['_children'], ++$r, $t['parent']); } } }
  • Prabhagaran
    Prabhagaran about 7 years
    also you can do like this, function printTree($tree, $r = 0, $p = 0)
  • jannej
    jannej about 6 years
    Is it not a bug here? Should it not be ($r + 1) instead of ++$r since it will also increase the "level" on the current node also? If you have a Test 1.2.1.1 the following 1.2.2 will get the same level if not using ($r + 1)