Trouble figuring out how to close tags using foreach loop

Go To StackoverFlow.com

0

I'm building a category manager via database. The following PHP generates the code I'll paste below that:

function generate_menu($parent, $menu_array, $result = NULL)
{
    $has_childs = FALSE;

    foreach($menu_array as $key => $value):
        if ($value['parent'] == $parent):
            $result .= '<li id="list_'.$value['id'].'" class="item">';
            $result .= '<div class="item">' . $value['name'] . '</div>';

            if ($has_childs):
                $has_childs = FALSE;
                $result .= '<ol>';
            else:
                $has_childs = TRUE;
                $result .= '</li>';
            endif; 

            $result .= $this->generate_menu($key, $menu_array);
        endif;
    endforeach;

    return $result;
}

And here's the HTML it generates:

<li id="list_11" class="item">
    <div class="item">Real Estate</div>
</li>
<li id="list_12" class="item">
    <div class="item">Home Improvements</div>
    <ol>
        <li id="list_13" class="item">
            <div class="item">Interior</div>
        </li>
        <li id="list_14" class="item">
            <div class="item">Exterior</div>
            <ol>
                <li id="list_15" class="item">
                    <div class="item">Exterior Subcat</div>
                </li>

So I'm able to close the <li> tag when it's a parent, but if it's an item with a parent, an <ol> enters the equation. That's when I don't know how to close them. I'm not sure where to put the </li> and </ol>. Everything I've tried just doesn't work.

Here's how it should look:

<li id="list_11" class="item">
    <div class="item">Real Estate</div>
</li>
<li id="list_12" class="item">
    <div class="item">Home Improvements</div>
    <ol>
        <li id="list_13" class="item">
            <div class="item">Interior</div>
        </li>
        <li id="list_14" class="item">
            <div class="item">Exterior</div>
            <ol>
                <li id="list_15" class="item">
                    <div class="item">Exterior Subcat</div>
                </li>
            </ol>
        </li>
    </ol>
</li>

EDIT:

Here is the code for passing the array and generating the menu:

$query = $this->db->get('categories');

foreach($query->result_array() as $row):
    $menu_array[$row['id']] = array('name' => $row['name'], 'parent' => $row['parent'], 'id' => $row['id']);
endforeach;

echo '<ol class="sortable">';
echo $this->tasks->generate_menu(0, $menu_array);
echo '</ol>';

And here's the print_r of $menu_array:

Array
(
    [11] => Array
        (
            [name] => Real Estate
            [parent] => 
            [id] => 11
        )

    [12] => Array
        (
            [name] => Home Improvements
            [parent] => 
            [id] => 12
        )

    [13] => Array
        (
            [name] => Interior
            [parent] => 12
            [id] => 13
        )

    [14] => Array
        (
            [name] => Exterior
            [parent] => 12
            [id] => 14
        )

    [15] => Array
        (
            [name] => Exterior Subcat
            [parent] => 
            [id] => 15
        )

)
2012-04-04 21:02
by dallen
Why are you passing $result to the function as an argument? And why are you using the alternative syntax? Also, what does the array look like - Madara Uchiha 2012-04-04 21:05
Not sure why I did that with $result, but I have since moved it into the function. That's isn't the problem though. I will append my post with the array, which, again, isn't the problem - dallen 2012-04-04 21:08
It's not the problem, but it's a part of the solution. If you want us to understand your problem, you need to tell us 5 things: What do you want? What have you tried? What are you inputting? What are you expecting? What are you getting? You've told us 4 of those - Madara Uchiha 2012-04-04 21:09
Don't you mean six? "And why are you using the alternative syntax? - dallen 2012-04-04 21:11
Heh, no that's really as a side note. Alternative syntax is meant usually for printing large blocks of HTML in between the PHP blocks (to prevent awkward <?php } ?> out of nowhere) - Madara Uchiha 2012-04-04 21:12
I didn't know that, but makes sense. I've always used it because I think it looks nice - dallen 2012-04-04 21:15
What I meant by show us the array, is take the complete array, print_r() it properly, and display the output here - Madara Uchiha 2012-04-04 21:16
I have edited the original post - dallen 2012-04-04 21:20
In the foreach loop you sholud check whether the
  • tag is a parent or it's an item with a parent. If you want to implement this method, you should add a boolean flag which will be an indicator for
  • - parent / tag with a parent. Once you do it, you'll be able to know when
      tags should be added in the right place. Hope you had found this answer helpfull - NoName 2012-04-04 21:21


  • 0

    I have figured this one out. I was complicating things a bit.

    function generate_menu($parent, $menu_array, $result = NULL)
    {       
        foreach($menu_array as $key => $value):
            if ($value['parent'] == $parent):
                $result .= '<li id="list_'.$value['id'].'" class="item">';
                $result .= '<div class="item">' . $value['name'] . '</div>';
                $result .= '<ol>';
                $result .= $this->generate_menu($key, $menu_array);
                $result .= '</li></ol>';
            endif;
        endforeach;
    
        return $result;
    }
    
    2012-04-04 22:49
    by dallen
    Your closing li and ol need to be the other way round :) Apart from that, as long as you don't have a lot of data to work with (and don't mind the empty ol's) this is a good solution I think. If the menu array becomes very large, you may get performance issues as you iterate through the whole array once per item - Armatus 2012-04-04 23:21


    0

    In order to use a recursive function properly, the array of values would need to reflect the hierarchy like so:

    array(
    [11] => "Real Estate",
    [12] => array(
        [0] => "Home Improvements",
        [13] => "Interior",
        [14] => "Exterior"
        ),
    [15] => "Exterior Subcat"
    )
    

    The function could now iterate through 11, adding a list item normally. At 12 it would encounter an array rather than a string (use gettype($value)), so instead of adding a list item it would call itself again - passing the array it just found as the argument. This allows in theory for an unlimited number of levels.

    function make_menu($menu_array) {
    
        $result = "<ol>";
    
        foreach($menu_array as $k=>$v)
        {
            $result .= '<li id="list_'.$k.'" class="item">';
    
            if(gettype($v) == 'array')
            {
                $result .= '<div class="item">' . $v[0] . '</div>';
                unset($v[0]);
                $result .= make_menu($v);
            }
            elseif(gettype($v) == 'string')
            {
                $result .= '<div class="item">' . $v . '</div>';
            }
    
            $result .= '</li>';
        }
        $result .= "</ol>";
        return $result;
    }
    
    2012-04-04 23:10
    by Armatus
    Ads