views:

139

answers:

2

I want to pull out menu items from MySQL.

Main menu           id=1, parentid=0
-Contact us         id=2, parentid=1
-Music              id=3, parentid=1
 --Rock             id=8, parentid=3
 --Classic          id=9, parentid=3
-Car                id=4, parentid=1
  --Toyota          id=5, parentid=4,
  --Ford            id=6, parentid=4,
  --Honda           id=7, parentid=4

Other menu          id=10, parentid=0
-Othermain          id=11, parentid=10
  --submenu         id=12, parentid=11

etc.

I can pullout data from id=1 to 4 and display by "...where parentid=1" etc. However this pulls out only the top level.

But I want to pullout all the data including submenu for each menu(main menu) as well.

Could anyone tell me how to write a query in MySQL for this please?

Thanks in advance.

+1  A: 

The fastest way is to fetch all elements from the table and build menu tree in the code side.

hsz
Could anyone tell me why this is wrong? It only uses one query, and it's the optimal solution for a navigation menu.
Daniel S
+1 I absolutely agree, for building a menu tree just one query is the way
rossoft
+4  A: 

You need to implement recursion to make repeated calls to the database to retrieve all children. You will have to replace my database abstraction layer implementation with your own but the concept is the same.

memcache solution

function generateTree($parentid = 0, &$tree) {
    $sql = sprintf('SELECT * FROM navigation WHERE parentid = %d', $parentid);
    $res = $this->db->results($sql);
    if ($res) {
        foreach ($res as $r) {
            // push found result onto existing tree
            $tree[$r->id] = $r;
            // create placeholder for children
            $tree[$r->id]['children'] = array();
            // find any children of currently found child
            $tree = generateTree($r->id, $tree[$r->id]['children']);
        }
    }
}

function getTree($parentid) {
    // memcache implementation
    $memcache = new Memcache();
    $memcache->connect('localhost', 11211) or die ("Could not connect"); 
    $tree = $memcache->get('navigation' . $parentid);
    if ($tree == null) {
        // need to query for tree
        $tree = array();
        generateTree($parentid, $tree);

        // store in memcache for an hour
        $memcache->set('navigation' . $parentid, $result, 0, 3600);
    }
    return $tree;
}

// get tree with parentid = 0
getTree(0);

non memcache solution

function generateTree($parentid = 0, &$tree) {
    $sql = sprintf('SELECT * FROM navigation WHERE parentid = %d', $parentid);
    $res = $this->db->results($sql);
    if ($res) {
        foreach ($res as $r) {
            // push found result onto existing tree
            $tree[$r->id] = $r;
            // create placeholder for children
            $tree[$r->id]['children'] = array();
            // find any children of currently found child
            $tree = generateTree($r->id, $tree[$r->id]['children']);
        }
    }
}

// get tree with parentid = 0
$tree = array();
$parentid = 0;
generateTree($parentid, $tree);

// output the results of your tree
var_dump($tree); die;

The above is untested so if anybody catches an error please let me know or feel free to update.

cballou
And here you generate `n` number of `SELECT` queries. It is not optimal solution.
hsz
@hsz - Personally I like to implement a caching solution to avoid performing such a db intensive function. Since navigation tends to be fairly static, it is easy to implement a permanent cache which is only deleted/invalidated when an administrative update to the navigation occurs.
cballou
@cballou - Ok, I agree with you - but we must have this caching implemented. But how about access to n cache files instead of one ? ;)
hsz
@hsz - You would be caching the returned array and not each individual level.
cballou
Can you give me without using memcache? I have never used it...
shin
@shin - I added it for you but I would advise you to do a search on memcache as it would speed up your navigation generation immensely.
cballou