Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people, just like you, are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
Solved

Recursive Tree PHP and Breadcrumbs

Posted on 2006-06-11
20
685 Views
Last Modified: 2013-12-12
Hello EE

I know somewhere is given answer to these questions, but I have no premium subscription.
Need a solution as function or class.

please NO GPL and do not post links from search engines, I need concrete solution (will not accept these answers)

I do have a DB table  pid, s_pid, menu_name, tooltip, content ...  (table name cms_pages)

s_pid is "parent" default value 0  and pid is "main"  

I need to render Recursive Tree menu (unlimited sub levels) from DB  and want to generate clean UL (unordered list xhtml valid)
As is on my CMS:
http://www.vision.to/CMS_BE/Home/ 

I need this functionality without using sessions or cookies and by inputting just one parameter example pid (the main index, node)

when parameter pid is requested even if it is sub or sub-sub item it should expand main, sub, sub   (making css class or id "current" for that sub and main node.

the result should be printed by function
example
echo my_menu($pid);

plus a solution if one main item does not exist or is deleted the sub items (if deleted main item)  should become as main

also I need the same way a breadcrumb

show current pid just getting one parameter ...

Start » Page 1 [Articles] » Sub-Page 1.3 [Articles] » test

That means the system should be able to find if current parameter is main item or if is sub or sub-sub-sub find it self the main item ...

waiting for you

0
Comment
Question by:feha
  • 13
  • 6
20 Comments
 
LVL 2

Author Comment

by:feha
ID: 16893014
For better understanding here is my function that does page navigation

// =======================================================================================================
function VISION_TO_PAGE_NAV($menu_option=2,$id="page-navigation",$expanded=0)
{

if(!CCGetGroupID()){CCSetSession(GroupID, 0);}
if(CCGetFromGet("wpid"))
{
CCSetSession("wpid".SUFFIX,CCGetFromGet("wpid"));
}
if(CCGetFromGet("p_node"))
{
CCSetSession("p_node".SUFFIX,CCGetFromGet("p_node"));
}


         global $cms_page_menu;
       $cms_page_menu = "";

$_PAGE_INDEX =FILE_NAME;
$Expand_Collapse="";
//$Expand_Collapse="<a href=\"$_PAGE_INDEX?p_expand=$menuStatus\" title=\"$menuStatus\" >$StatusText</a>";
      switch($menu_option)
{
  case 1: return VISION_TO_TRANSLATE("<div id=\"$id\">$Expand_Collapse<!-- dynamic page_menu [1] by www.vision.to -->\n".str_replace("<ul></ul>","",VISION_TO_PAGE_NAV_1($_PAGE_INDEX,$parent=0,$expanded))."</div>"); break;
  case 2: return VISION_TO_TRANSLATE("<div id=\"$id\">$Expand_Collapse<!-- dynamic page_menu [2] by www.vision.to -->\n".str_replace("<ul></ul>","",VISION_TO_PAGE_NAV_2($_PAGE_INDEX,$parent=0,$expanded))."</div>"); break;
  case 3: return VISION_TO_TRANSLATE("<div id=\"$id\">$Expand_Collapse<!-- dynamic page_menu [3] by www.vision.to -->\n".str_replace("<ul></ul>","",VISION_TO_PAGE_NAV_3($_PAGE_INDEX,$parent=0,$expanded))."</div>"); break;
  default: return VISION_TO_TRANSLATE("<div id=\"$id\">$Expand_Collapse<!-- dynamic page_menu [default] by www.vision.to -->\n".str_replace("<ul></ul>","",VISION_TO_PAGE_NAV_1($_PAGE_INDEX,$parent=0,$expanded))."</div>");
}

}
//======================================================================================================================//


//======================================================================================================================//
//                           MENU 2
//======================================================================================================================//
function VISION_TO_PAGE_NAV_2($_PAGE_INDEX,$parent=0,$expanded=0)
{
//Expand On pid
global $cms_page_menu;
global $CMS_CONFIG;
if(!CCGetSession("front_id".SUFFIX))
{
CCSetSession("front_id".SUFFIX,DEFAULT_FRONT_SECTION);
}

$db = new clsDBcms();

if(isset($_SERVER['HTTP_USER_AGENT']) && eregi($CMS_CONFIG['CRAWLERS'],$_SERVER['HTTP_USER_AGENT']))
{
$SQL = "SELECT pid, s_pid, front_id, page_title, menu_name  FROM cms_pages WHERE  active=1  AND s_pid = ".$db->ToSQL($parent,ccsInteger)." AND group_id = 0  ORDER BY display_order, menu_name";
}
elseif(CCGetSession("front_id".SUFFIX))
{

$SQL = "SELECT pid, s_pid, front_id, page_title, menu_name  FROM cms_pages WHERE  active=1 AND s_pid = ".$db->ToSQL($parent,ccsInteger)." AND group_id <= ".$db->ToSQL(CCGetGroupID(),ccsInteger)." AND lang_id=".$db->ToSQL(CCGetSession(lang), ccsText)." AND front_id=".$db->ToSQL(CCGetSession("front_id".SUFFIX),ccsInteger)." ORDER BY display_order, menu_name";


}
else
{

      $SQL = "SELECT pid, s_pid, front_id, page_title, menu_name FROM cms_pages WHERE active=1 AND s_pid = ".$db->ToSQL($parent,ccsInteger)." AND  group_id <= ".$db->ToSQL(CCGetGroupID(),ccsInteger)." AND lang_id=".$db->ToSQL(CCGetSession(lang), ccsText)."  ORDER BY display_order, menu_name";

}
    $res = $db->query($SQL);
    if($res > 0)
      {  
        $cms_page_menu .="<ul>";
        while($db->next_record())
            {
$current_link = "";
$current_li = "";

//if(eregi(basename($_page_index),basename (FILE_NAME)) || CCGetSession("pid".SUFFIX)==$db->f("pid"))

if(CCGetSession("p_node".SUFFIX)==$db->f("pid") || CCGetFromGet("p_node")==$db->f("pid") || CCGetSession("wpid".SUFFIX)==$db->f("pid") || CCGetFromGet("wpid")==$db->f("pid"))
{
$current_link=" class=\"current_page\" ";
$current_li=" class=\"active_page\" ";
}
/*
           if($db->f("s_pid") == 0)
               {
                $cms_page_menu .="<li$current_li><a href=\"".$_PAGE_INDEX."?wpid/".$db->f("pid")."/p_node/".$db->f("pid")."/edit_pid/".$db->f("pid")."/front_id/".$db->f("front_id")."\" title=\"".$db->f("page_title")."\" $current_link>".$db->f("menu_name")."</a>";
                  }
                  else
                  {
                  $cms_page_menu .="<li$current_li><a href=\"".$_PAGE_INDEX."?pid/".$db->f("s_pid")."/wpid/".$db->f("pid")."/p_node/".$db->f("s_pid")."/edit_pid/".$db->f("pid")."/front_id/".$db->f("front_id")."\" title=\"".$db->f("page_title")."\" $current_link>".$db->f("menu_name")."</a>";
                  }
*/

           if($db->f("s_pid") == 0)
               {
                $cms_page_menu .="<li$current_li><a href=\"".$_PAGE_INDEX."?wpid=".$db->f("pid")."&p_node=".$db->f("pid")."&edit_pid=".$db->f("pid")."&front_id=".$db->f("front_id")."\" title=\"".$db->f("page_title")."\" $current_link>".$db->f("menu_name")."</a>";
                  }
                  else
                  {
                  $cms_page_menu .="<li$current_li><a href=\"".$_PAGE_INDEX."?pid=".$db->f("s_pid")."&wpid=".$db->f("pid")."&p_node=".$db->f("s_pid")."&edit_pid=".$db->f("pid")."&front_id=".$db->f("front_id")."\" title=\"".$db->f("page_title")."\" $current_link>".$db->f("menu_name")."</a>";
                  }


if(isset($_SERVER['HTTP_USER_AGENT']) && eregi($CMS_CONFIG['CRAWLERS'],$_SERVER['HTTP_USER_AGENT']))
{
VISION_TO_PAGE_NAV_2($_PAGE_INDEX, $db->f("pid"),$expanded);
}

elseif(CCGetSession("p_node".SUFFIX)==$db->f("pid"))
{          
                VISION_TO_PAGE_NAV_2($_PAGE_INDEX, $db->f("pid"),$expanded);
}
/*
else
{
VISION_TO_PAGE_NAV_2($_PAGE_INDEX, $db->f("pid"));
}
*/

            $cms_page_menu .="</li>\n";
        }
        $cms_page_menu .="</ul>";
    }
      $db->close();
      return $cms_page_menu;
}
//======================================================================================================================//


the first function is "Switcher" ...

here is used input param (wpid)  and sesions ...

0
 
LVL 3

Expert Comment

by:NewJorg
ID: 16893840
Hi,
based on your question I found this solution. I hope I have understood it correctly. I have not read your comment, but I will do it later at home. w3.org said it is valid xhtml1.1 (the menu-tree). You can use it in a single sample page. It also contains code for some demo db-entries I used.

<!DOCTYPE html
        PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml1i1.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="de">
<head>
<title>Testpage</title>
</head>
<body>
<div>
<?php
include("../include/adodb/adodb.inc.php");
$db=NewADOConnection("mysql");
$db->Connect("dbhost","dbuser","dbpass","dbname");

$id=7; // current selected

function my_menu ($aid=0, $pid=0, $level=0)
{
        // need a local copy for using foreach
        $menu_local = $GLOBALS['menu'];
        $found_node = false;
        $html = "";
        foreach($menu_local as $node)
        {
                if($node['s_pid'] == $pid)
                {
                        if(!$found_node)
                        {
                                $html.="<ul>\n";
                                $found_node = true;
                        }
                        if($node['pid'] == $aid)
                        {
                                $active=' id="active"';
                        }
                        else
                        {
                                $active='';
                        }
                        //$html.=sprintf("%s<li%s>%s</li>\n",
                        //      str_repeat('&nbsp;', $level),$active,$node['menu_name']);
                        $html.=sprintf("<li%s>%s</li>\n",
                                $active,$node['menu_name']);
                        $submenu=my_menu($aid, $node['pid'], $level+1);
                        if(!empty($submenu))
                        {
                                $html.="<li>".$submenu."</li>\n";
                        }
                }
        }
        if($found_node)
        {
                $html.="</ul>\n";
        }
        return $html;
}

function load_menu ($db)
{
        $GLOBALS['menu'] = Array();

        $query = "SELECT pid, s_pid, menu_name, tooltip FROM cms_pages";
        $db->SetFetchMode(ADODB_FETCH_ASSOC);
        $rs = $db->Execute($query);
        while(!$rs->EOF)
        {
                $GLOBALS['menu'][$rs->fields['pid']] = $rs->fields;
                $rs->MoveNext();
        }

}

function backlink ($pid)
{
        if($pid != 0)
        {
                $html=backlink($GLOBALS['menu'][$pid]['s_pid']);
                $html.=" --&gt; ".$GLOBALS['menu'][$pid]['menu_name'];
        }
        else
        {
                $html="Home";
        }
        return $html;
}

function find_orphan($db,$correct=false)
{
        foreach($GLOBALS['menu'] as $node)
        {
                if(!isset($GLOBALS['menu'][$node['s_pid']]) && $node['s_pid'] != 0)
                {
                                    if($correct)
                                    {
                                          $query = "UPDATE cms_pages SET s_pid=0 WHERE pid=".$node['pid'];
                                          $db->Execute($query);
                                    }
                                    else
                                    {
                                          echo "<div>Orphan: ".$node['pid']."</div>";
                                    }
                }
        }
}

function create_table($db)
{
        $query = "CREATE TABLE cms_pages (
                pid INT PRIMARY KEY,
                s_pid INT NOT NULL DEFAULT 0,
                menu_name VARCHAR(30),
                tooltip VARCHAR(255))";
        $db->Execute($query);
}

function fill_data($db)
{
        $query = "INSERT INTO cms_pages VALUES (1,0,'Main 1', 'Main 1')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (2,0,'Main 2', 'Main 2')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (3,0,'Main 3', 'Main 3')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (4,1,'Sub 1 Main 1', 'Sub 1 Main 1')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (5,1,'Sub 2 Main 1', 'Sub 2 Main 1')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (6,5,'Sub 1 Sub 2 Main 1', 'Sub1 Sub 2 Main 1')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (7,5,'Sub 2 Sub 2 Main 1', 'Sub 2 Sub 2 Main 1')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (8,1,'Sub 3 Main 1', 'Sub 3 Main 1')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (9,2,'Sub 1 Main 2', 'Sub 1 Main 2')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (10,2,'Sub 2 Main 2', 'Sub 2 Main 2')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (11,0,'Main 4', 'Main 4')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (12,11,'Sub 1 Main 4', 'Sub 1 Main 4')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (13,12,'Sub 1 Sub 1 Main 4', 'Sub 1 Sub 1 Main 4')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (14,13,'Sub 1 Sub 1 Sub 1 Main 4', 'Sub 1 Sub 1 Sub 1 Main 4')";
        $db->Execute($query);
        $query = "INSERT INTO cms_pages VALUES (16,15,'Sub 1 Main 5', 'help me I am orphan')";
        $db->Execute($query);
}

// create_table($db);
// fill_data($db);
load_menu($db);

// show orphan
find_orphan($db);

// Correct orphan
find_orphan($db,true);


// HERE is your $id used
echo backlink($id);
echo my_menu($id);
?>
</div>
</body>
</html>
0
 
LVL 2

Author Comment

by:feha
ID: 16898418
im testing your script

breadcrumbs are ok and working great ...

the tree should expand only the "section" of $id example 7


if id 7
than
Main 1

Sub 1 Main 1
Sub 2 Main 1

Sub 1 Sub 2 Main 1
Sub 2 Sub 2 Main 1
Sub 3 Main 1

should stay expanded
and otherr should be as main (not expanded) if possible as option

this is 90 % OK

0
Free Tool: Postgres Monitoring System

A PHP and Perl based system to collect and display usage statistics from PostgreSQL databases.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 3

Accepted Solution

by:
NewJorg earned 500 total points
ID: 16899975
Hi, I added one function

function get_active_path($pid)
{
        $active=array();
        if($pid != 0)
        {
                $active[]=$pid;
                $active=array_merge($active,get_active_path($GLOBALS['menu'][$pid]['s_pid']));
        }
        return $active;
}

and modified this one

function my_menu ($aid=0, $pid=0, $level=0, $expand=false)
{
        // need a local copy for using foreach
        $menu_local = $GLOBALS['menu'];
        $found_node = false;
        $html = "";
        foreach($menu_local as $node)
        {
                $expand_local = $expand;
                if($node['s_pid'] == $pid)
                {
                        if(in_array($node['pid'],$GLOBALS['active_path']))
                        {
                                $expand_local=true;
                        }
                        if($expand || ($node['s_pid'] == 0))
                        {
                                if(!$found_node)
                                {
                                        $html.="<ul>\n";
                                        $found_node = true;
                                }
                                if($node['pid'] == $aid)
                                {
                                        $active=' id="active"';
                                }
                                else
                                {
                                        $active='';
                                }
                                $html.=sprintf("<li%s>%s</li>\n",
                                        $active,$node['menu_name']);
                                $submenu=my_menu($aid, $node['pid'],
                                        $level+1,$expand_local);
                                if(!empty($submenu))
                                {
                                        $html.="<li>".$submenu."</li>\n";
                                }
                        }
                }
        }
        if($found_node)
        {
                $html.="</ul>\n";
        }
        return $html;
}

$GLOBALS['active_path']=get_active_path($id);

echo my_menu($id);
will print:
    * Main 1
    *
          o Sub 1 Main 1
          o Sub 2 Main 1
          o
                + Sub 1 Sub 2 Main 1
                + Sub 2 Sub 2 Main 1
          o Sub 3 Main 1
    * Main 2
    * Main 3
    * Main 4
    * Sub 1 Main 5 <-- was orphan now a Main

echo my_menu($id,0,0,true);
will show the whole menu
    * Main 1
    *
          o Sub 1 Main 1
          o Sub 2 Main 1
          o
                + Sub 1 Sub 2 Main 1
                + Sub 2 Sub 2 Main 1
          o Sub 3 Main 1
    * Main 2
    *
          o Sub 1 Main 2
          o Sub 2 Main 2
    * Main 3
    * Main 4
    *
          o Sub 1 Main 4
          o
                + Sub 1 Sub 1 Main 4
                +
                      # Sub 1 Sub 1 Sub 1 Main 4
    * Sub 1 Main 5


0
 
LVL 2

Author Comment

by:feha
ID: 16902814
Thank you very much , well done.
0
 
LVL 2

Author Comment

by:feha
ID: 16910258
i just have problems with this function

function load_menu ($db)
{
        $GLOBALS['menu'] = Array();

        $query = "SELECT pid, s_pid, menu_name, tooltip FROM cms_pages";
        $db->SetFetchMode(ADODB_FETCH_ASSOC);
        $rs = $db->Execute($query);
        while(!$rs->EOF)
        {
                $GLOBALS['menu'][$rs->fields['pid']] = $rs->fields;
                $rs->MoveNext();
        }

}

I'm not using ADODB connection or warapper ....

The model im using is :
function load_menu ($db)
{
        $GLOBALS['menu'] = Array();

        $SQL = "SELECT pid, s_pid, menu_name, tooltip FROM cms_pages";
        $db->query($SQL);
        while($db->next_record())
        {
               // $GLOBALS['menu'][$rs->fields['pid']] = $rs->fields;
/* =====================================================
       $db->f("pid")  ,  $db->f("s_pid")  ,  $db->f("menu_name")   etc ...
How to put these in coorrect array in order to get menu working
======================================================= */
         
         }

}
0
 
LVL 3

Expert Comment

by:NewJorg
ID: 16910375
$GLOBALS['menu'][$db->f("pid")]=Array("pid" => $db->f("pid"), "s_pid" => $db->f("s_pid"), "menu_name" => $db->f("menu_name"));
should work
0
 
LVL 2

Author Comment

by:feha
ID: 16910434
Thank You very much :-)
0
 
LVL 2

Author Comment

by:feha
ID: 16910721
i get Notice: Undefined offset: 1 in c:\inetpub\wwwroot\php4\CMS_UNIVERSE\Functions\website_menu_func.php on line 40

if i set $pid='7'; i get same error with corresponding pid ...

the function i changed to work with my DB is:

function load_menu ()
{

$db = new clsDBcms();

        $GLOBALS['menu'] = Array();

        $SQL = "SELECT pid, s_pid, menu_name, tooltip FROM cms_pages WHERE active=1";
            $db->query($SQL);
            while($db->next_record())
        {

            $GLOBALS['menu'][$db->f("pid")]=Array("pid" => $db->f("pid"), "s_pid" => $db->f("s_pid"), "menu_name" => $db->f("menu_name"));

        }
$db->close();
}

i think there is problem pasing array
Note:I'm not using here any longer ADODB ...

0
 
LVL 3

Expert Comment

by:NewJorg
ID: 16910769
Which line is the line 40? in the website_menu_func.php?

When you will use your tooltips you should add "tooltip" => $db->f("tooltip") to the array.
0
 
LVL 2

Author Comment

by:feha
ID: 16911007
line 40
                $html=backlink($GLOBALS['menu'][$pid]['s_pid']);    
line41
$html.=" --&gt; ".$GLOBALS['menu'][$pid]['menu_name'];

line118
$active=array_merge($active,get_active_path($GLOBALS['menu'][$pid]['s_pid']));


error's
Notice: Undefined offset: 1 in c:\inetpub\wwwroot\php4\CMS_UNIVERSE\Functions\website_menu_func.php on line 40

Notice: Undefined offset: 1 in c:\inetpub\wwwroot\php4\CMS_UNIVERSE\Functions\website_menu_func.php on line 41
Home -->
Notice: Undefined offset: 1 in c:\inetpub\wwwroot\php4\CMS_UNIVERSE\Functions\website_menu_func.php on line 118


0
 
LVL 2

Author Comment

by:feha
ID: 16911330
Ok
got it to work

the problem was my old cms_pages table structure ...

is ther any problem if s_pid is empty (NULL) ?

this will work ok
thannk you
0
 
LVL 2

Author Comment

by:feha
ID: 16912520
my last question

im in process adding links

$html.=sprintf("<li%s>%s</li>\n",

?

I do have probem to add links this way ....

link should be  index.php?pid=  ... and title will have tooltip text

0
 
LVL 3

Expert Comment

by:NewJorg
ID: 16912657
I think there will be no problem with s_pid being NULL only that find orphan will probably set the NULL to 0 (main element)

an link will be easy, look a bit in the sprintf php documentation
$html.=sprintf("<li%s><a href=\"%s\" title=\"%s\">%s</a></li>\n",
        $active,$url,$node['tooltip'],$node['menu_name']);
should work.
0
 
LVL 2

Author Comment

by:feha
ID: 16912747
Thank You
I don't use much sprintf  (except for time things)   :-), but will read about it.

Thank you very much for your great help.
0
 
LVL 2

Author Comment

by:feha
ID: 16912801
fixed
works great

$url="index.php?pid=".$node['pid'];
$html.=sprintf("<li%s><a href=\"%s\" title=\"%s\">%s</a></li>\n", $active,$url,$node['tooltip'],$node['menu_name']);

:-)
0
 
LVL 2

Author Comment

by:feha
ID: 16919860
sorry to bother you ...

i still have problems when trying to use sections and languages ...

if system does not find any records it shows undefined index errors ...

Source code is here
http://www.vision.to/test/website_menu_func.phps

commented querries does not work if no record found ...
when trying to select by lang_id or front_id ...

here is working example of my old menu solution:
http://trinityhome.org/~visionto/Home/index.php?front_id=1

I just want similar put with single pid ...

else works ok ...
if you want i can open another question and give 250 points ...

0
 
LVL 3

Expert Comment

by:NewJorg
ID: 16920288
I dont really get the problem at the moment, an new question would be the best because of more people will read it and maybe someone will catch the problem and present you an answer.
0
 
LVL 2

Author Comment

by:feha
ID: 16920985
ok thank you :-)
0

Featured Post

Free Tool: Path Explorer

An intuitive utility to help find the CSS path to UI elements on a webpage. These paths are used frequently in a variety of front-end development and QA automation tasks.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Suggested Solutions

This article will explain how to display the first page of your Microsoft Word documents (e.g. .doc, .docx, etc...) as images in a web page programatically. I have scoured the web on a way to do this unsuccessfully. The goal is to produce something …
Nothing in an HTTP request can be trusted, including HTTP headers and form data.  A form token is a tool that can be used to guard against request forgeries (CSRF).  This article shows an improved approach to form tokens, making it more difficult to…
Explain concepts important to validation of email addresses with regular expressions. Applies to most languages/tools that uses regular expressions. Consider email address RFCs: Look at HTML5 form input element (with type=email) regex pattern: T…
The viewer will learn how to create and use a small PHP class to apply a watermark to an image. This video shows the viewer the setup for the PHP watermark as well as important coding language. Continue to Part 2 to learn the core code used in creat…

839 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question