tags:

views:

1946

answers:

5

Hello,

I am attemptting to attach a small CMS to a website I am creating. However I have come across a small problem. The CMS uses PHP functions for inserting menus, these PHP functions create the HTML. The particular function I wish to use (treemenu) creates a nested ul li that can then be used for a drop down menu. However the nested ul li is structured like so:

<li>Projects (Menu Level 1)</li>
    <ul>
        <li>Project 1 (Menu Level 2)</li>
        <li>Project 2 (Menu Level 2)</li>
        <li>Project 3 (Menu Level 2)</li>
    </ul>
<li>News (Menu Level 1)</li>
<li>Contact (Menu Level 1)</li>

When creating a drop down menu in CSS I believe the Menu Level 1 li should wrap its children like so:

<li>Projects (Menu Level 1)
    <ul>
        <li>Project 1 (Menu Level 2)</li>
        <li>Project 2 (Menu Level 2)</li>
        <li>Project 3 (Menu Level 2)</li>
    </ul>
</li>
<li>News (Menu Level 1)</li>
<li>Contact (Menu Level 1)</li>

I have never before worked with PHP and therefore would not know how to alter the function in order to accomplish the above. I would hope it would be a simple change. Below is the PHP function that outputs the first example structure:

function treemenu($generat=0) {
global $pagenum, $menu, $selected, $extension, $set;
$count=0;
$out="\n";
$intend=0;
while($menu[$count][0] != "") {
 if(strpos($menu[$count][3],"#") === false) {
 if($menu[$count][2]=="0" && $intend==2) {
  $intend--;
  $out.="</ul>\n";
 }
 if($menu[$count][1]=="0" && $intend==1) {
  $intend--;
  $out.="</ul>\n";
 }
 if($menu[$count][1]!="0" && $intend<1) {
  $intend=1;
  $out.="<ul>\n";
 }
 if($menu[$count][2]!="0" && $intend<2) {
  $intend=2;
  $out.="<ul>\n";
 }
 $out.="<li class=\"LNE_menu\"><a ";
 if($menu[$count][4]==$selected['name'])
  $out.= 'class="selected" ';
 if(strpos($menu[$count][3],"*"))
  $out.='href="'.str_replace("*", "",$menu[$count][3]).'">';
 elseif($generat)
  $out.='href="'.$menu[$count][3].".".$set['extension'].'">';
 else
  $out.='href="'.$set['indexfile'].'?page='.$menu[$count][3].'">';
 $out.=$menu[$count][4]."</a></li>\n";
 }
 $count++;
}
return $out;
}

Could anyone possibly point me in the right direction as to how to make the closing li tag of a level 1 menu item wrap the ul immediately after, as in the second example?

+7  A: 

This would be a excellent example of the use of recursion. An array (with sub-arrays within it) defines each level, and a function loops, calling itself whenever it finds a new array to process. As long as the function cleans up appropriately (closing the </li> & </ol>), it's largely automatic.

<?php
// I know which function I'd rather write....
$tree = array('Projects (Menu Level 1)',
              array('Project 1 (Menu Level 2)',
                    'Project 2 (Menu Level 2)',
                    'Project 3 (Menu Level 2)'),
              'News (Menu Level 1)',
              'Contact (Menu Level 1)');

// now quake beneath the power of this fully operational recursive function!
function olLiTree($tree)
{
    echo '<ul>';
    foreach($tree as $item) {
        if (is_array($item)) {
            olLiTree($item);
        } else {
            echo '<li>', $item, '</li>';
        }
    }
    echo '</ul>';
}
olLiTree($tree);  // kick off from the top level
Alister Bulman
You’re generating invalid HTML. The nested `<ul>` needs to be inside a `<li>`.
Gumbo
i'd avoid echoing inside the function, bad style imho.
Schnalle
Alister Bulman
+1  A: 

It appears Topbit already beat me to this, but mine is slightly differs in that it doesn't echo the value straight to the output stream, but saves it in a variable that you may echo at your convenience:

<?php

function GenerateMenu($arr)
{
    $output = "<ul>\n";
    foreach($arr as $val) {
     if (is_array($val)) {
      $output .= "<li>\n" . GenerateMenu($val, $output) . "</li>\n";
     }
     else {
      $output .= "<li>" . $val . "</li>\n";
     }
    }
    $output .= "</ul>\n";
    return $output;
}

$html = GenerateMenu($menu);

?>

Edit:

Thanks Gumbo and Topbit, now that I'm on my machine with PHP installed, I have tested it and it works fine.

John Rasch
replacing +'s with the string concatenation '.' does make it work.
Alister Bulman
You’re generating invalid HTML. The nested `<ul>` needs to be inside a `<li>`.
Gumbo
PHP string concatenation is . not +. And yes, a function should imo generally not output any data directly.
OIS
+3  A: 

A simple function is all you need.

function unorderedList($val)
{
    if (is_array($val)) {
     return '<ul><li>' . 
       implode('</li><li>', 
        array_map('unorderedList', $val)) . 
      '</li></ul>';
    } else {
     return $val;
    }
}

Test code:

$menu = array('Projects (Menu Level 1)',
              array('Project 1 (Menu Level 2)',
                    'Project 2 (Menu Level 2)',
                    'Project 3 (Menu Level 2)'),
              'News (Menu Level 1)',
              'Contact (Menu Level 1)');


echo unorderedList($menu);
OIS
Thanks OIS, I understand that this is all I need now. However I do not have the knowledge to implement this as I would need to take into account other variables such as $pagenum, $menu, $selected, $extension, $set, which are used. I wish i could simply alter something in the original code.
Ronnie
A: 
A: 

I think this outputs the correct HTML structure.

function GenerateMenu($arr)
{
foreach($arr as $val) {
 if (next($arr)) { $nextitem = current($arr); }
 if (is_array($val)) {
  $output .= "\n<ul>\n" . GenerateMenu($val, $output) . "</ul>\n</li>\n\n";
 }
 elseif (is_array($nextitem)) { $output .= "<li>" . $val; }
 else { $output .= "<li>" . $val . "</li>\n"; }
}
return $output;
}
$html = GenerateMenu($menu);
echo("<ul>\n" . $html . '</ul>');

Same test code as above:

$menu = array('Projects (Menu Level 1)',
          array('Project 1 (Menu Level 2)',
                'Project 2 (Menu Level 2)',
                'Project 3 (Menu Level 2)'),
          'News (Menu Level 1)',
          'Contact (Menu Level 1)');
Karina