tags:

views:

97

answers:

3

Hiya,

I'm trying to create an array of arrays so I can build a dynamic menu, but I'm getting lost amongst the code. My output looks like this:

$menu = Array ( 
        [0] => Array ( 
            [text] => Home 
            [class] => 875 
            [link] => //Home 
            [show_condition] => TRUE 
            [parent] => 0 
            ) 
        [1] => Array ( 
            [text] => About 
            [class] => 326 
            [link] => //About 
            [show_condition] => TRUE 
            [parent] => 0 
            ) 
         etc 
         etc 
         etc       
        [339] => Array ( 
            [text] => Planner 
            [class] => 921 
            [link] => //Planner 
            [show_condition] => TRUE 
            [parent] => 45 
            ) 
    )

And the two functions which should build the menu are:

    function build_menu ( $menu )   {
         $out = '<div class="container4">' . "\n";
         $out .= ' <div class="menu4">' . "\n";
         $out .= "\n".'<ul>' . "\n";

         for ( $i = 1; $i <= count ( $menu )-1; $i++ )
         {

if ( is_array ( $menu [ $i ] ) ) {//must be by construction but let's keep the errors home
           if ( $menu [ $i ] [ 'show_condition' ] && $menu [ $i ] [ 'parent' ] == 0 ) {//are we allowed to see this menu?
            $out .= '<li class="' . $menu [ $i ] [ 'class' ] . '"><a href="' . $menu [ $i ] [ 'link' ] . '">';
            $out .= $menu [ $i ] [ 'text' ];
            $out .= '</a>';
            $out .= get_childs ( $menu, $i );
            $out .= '</li>' . "\n";
           }
          }
          else {
           die ( sprintf ( 'menu nr %s must be an array', $i ) );
          }
         }

         $out .= '</ul>'."\n";
         $out .= "\n\t" . '</div>';
         return $out . "\n\t" . '</div>';
        }

    function get_childs ( $menu, $el_id )   {
         $has_subcats = FALSE;
         $out = '';
         $out .= "\n".' <ul>' . "\n";
         for ( $i = 1; $i <= count ( $menu )-1; $i++ )
         {

          if ( $menu [ $i ] [ 'show_condition' ] && $menu [ $i ] [ 'parent' ] == $el_id ) {//are we allowed to see this menu?
           $has_subcats = TRUE;
           $add_class = ( get_childs ( $menu, $i ) != FALSE ) ? ' subsubl' : '';
           $out .= '  <li class="' . $menu [ $i ] [ 'class' ] . $add_class . '"><a href="' . $menu [ $i ] [ 'link' ] . '">';
           $out .= $menu [ $i ] [ 'text' ];
           $out .= '</a>';
           $out .= get_childs ( $menu, $i );
           $out .= '</li>' . "\n";
          }
         }
         $out .= ' </ul>'."\n";
         return ( $has_subcats ) ? $out : FALSE;
        }

But the menu is refusing to show any submenu levels - it only displays top level. Any ideas?

Thanks!

+1  A: 

Your code is almost there - you may want to change mysql_fetch_array to mysql_fetch_assoc, and you can convert values as returned into the appropriate types using functions like intval:

$menu = array();
$sql = "SELECT TabName as text, TabID as class, TabPath as link, IsVisible as show_condition, ParentId as parent FROM dnn_SMA_Tabs WHERE PortalID = 3 AND IsVisible = 'True' ORDER BY TabOrder ASC";
$result = mysql_query($sql);
$index = 1;
while($row = mysql_fetch_assoc($result)) {
    $row['parent'] = intval($row['parent']);
    $menu[$index] = $row;
    $index++;
}

You'll need to convert show_condition to the appropriate type - how to do that probably depends on what column type IsVisible is.

Dominic Rodger
I think it's really close to working, but I now get this error messageNotice: Undefined offset: 373 in sitemap2.php on line 24menu nr 373 must be an arrayThe query returns 373 rows, so the very last one is doing something funny. Any ideas?
hfidgen
What's line 24 of `sitemap2.php`?
Dominic Rodger
edited it all into my answer
hfidgen
Hmmm... It seems to be putting things into menu with indices starting at 0 instead of 1. Could you adjust your for-loops accordingly (to `for ( $i = 0; $i < count ( $menu ); $i++ )`)
Dominic Rodger
In php arrays start from offset 0, the suggestion in Dominic comment will fix the error.
Davide Gualano
That breaks all output somehow, but making count($menu)-1 seems to work. However, new problem - it's not generating any child menus whatsoever, only the top level :x
hfidgen
+1 for off-by-one problem pointed out by Dominic. You might want to change this line `for( $i = 1; $i <= count ( $menu ); $i++ )` to this `for( $i = 0; $i < count ( $menu ); $i++ )`
andyk
@Andy tried that, but it breaks all output - page tries to download rather than display. The -1 thing I mentioned seems to be the only workaround.
hfidgen
A: 

i see your array has indexes from [0] to [399]

Array (

[0] => Array ( 
    [text] => Home 
    [class] => 875 
    [link] => //Home 
    [show_condition] => TRUE 
    [parent] => 0 
    ) 
[1] => Array ( 
    [text] => About 
    [class] => 326 
    [link] => //About 
    [show_condition] => TRUE 
    [parent] => 0 
    ) 
 etc 
 etc 
 etc       
[339] => Array ( 
    [text] => Planner 
    [class] => 921 
    [link] => //Planner 
    [show_condition] => TRUE 
    [parent] => 45 
    )  )

but you try to show items from [1] to [340]

for ( $i = 1; $i <= count ( $menu ); $i++ )

count($menu) returns 340 ([0]->[399])

Solution: for ( $i = 0; $i < count ( $menu ); $i++ )

start from 0 and go until 399 (strictly < 340)

Hi, my array only goes to 340, not quite sure where you're getting 399 from! The count problem was identified in the above answer too, but it's not quite fixed things.
hfidgen
A: 

I would do it in an other way: object oriented.

class Menu {
  private $children = array();
  private $name = '';
  private $link = '';
  private $class = '';
  private $show = TRUE;

  function __construct($name, $class, $link, $show = TRUE, $parent = null) {
    $this->name = $name;
    $this->link = $link;
    $this->class = $class;
    $this->show = $show;
    if(!is_null($parent)) {
        $parent->addChild($this);
    }
  }

  function addChild(Menu $child) {
      $this->children[] = $child;
  }

  // ... remaining getters (and probably setters)
}

Then you can build the menu like this:

$home = new Menu('Home', '875', '//Home');
$about = new Menu('About', '326', '//About');

//...

$planner = new Menu('Planner', '921', '//Planner', true, $home);

$menu = array($home, $about,...);

This is just one example. I am aware that this would mean you create 340 variables to hold your menu. With other setter and getter methods you can do it better, this is just a fast 'sketch'.

You can build the menu like this:

function build_menu ( $menu, $showContainer = false)   {
    $out = '';
    if($showContainer) {
        $out = '<div class="container4">' . "\n";
        $out .= '       <div class="menu4">' . "\n";              
    }

    if(!empty($menu)) {
        $out .= '<ul>' . "\n";

        for ($entry in  $menu) {
            if($entry->getShow()) {//are we allowed to see this menu?
                 $out .= '<li class="' . $entry->getClass() . '"><a href="' . $entry->getLink() . '">';
                 $out .= $entry->getText();
                 $out .= '</a>';
                 $out .= "\n" . build_menu($entry->getChildren());
                 $out .= '</li>' . "\n";
             }
        }
        $out .= '</ul>'."\n";
    }
    if($showContainer) {
        $out .= "\n\t" . '</div>';
        $out .= "\n\t" . '</div>';                
    }
    return $out;
}

I didn't test the code, but this is the idea behind it. If you have no experience with OOP and php have a look at the official documentation.

And also note that this requires PHP5.

Felix Kling