3

我正在寻找一种从 mysql 数据库中提取数据以创建无限子类别菜单的方法。菜单中有大约 1500 个类别,它们以下列格式存储到数据库表(菜单)中:

category_id , 类别标题 , parent_id

我想做 1 mysql 查询将所有数据放入一个数组中。

$menu_list =array();

$query = mysql_query("SELECT * FROM menu ORDER BY category_id ASC");

if(mysql_num_rows($query) != 0) {
        while($row = mysql_fetch_array($query)){
              $category_id = $row['category_id'];
              $title = $row['category_title'];
              $parent_id = $row[parent_id'];
              $menu_list[] = array($category_id,$title,$parent_id);
        }
}

那是我目前用来将数据转换为数组的代码。

然后我需要遍历数组来建立一个菜单。

然后我将隐藏所有子类别并使用 jquery 扩展(我可以做到这一点没问题)

我遇到的问题是遍历数组并以正确的顺序显示数据。该菜单有无限的子类别,因此它可能需要一个递归函数来检索数据。我已经关注了这里的很多例子,但后来我迷失了如何显示数据..

我现在需要结束:

main item
  sub cat
  sub cat
      sub cat 
          sub cat 
main item
   sub cat
main item
main item
   sub cat
       sub cat
          sub cat
             sub cat
             sub cat

etc...  

每个类别都在自己的 div 中,然后我需要能够编辑每个类别(即标题名称和位置,如果需要)然后如果我更改子类别的位置,快速页面刷新将以新顺序重新加载菜单..

最终我想把它变成一个拖放,但这将在以后的日期。

我希望这已经足够解释了..

提前致谢..


所以问题又回来困扰我了......我的ajax调用另一个mysql选择的方法对于CMS部分来说是可以的,因为它只是不时使用。但是现在我们已经到了前端。每次查看页面时,都会使用大量 mysql 请求来拉取菜单的前 3 级。随着类别越来越大,这导致 1600 多个类别被多次提取,即使每天 1000 次访问也会导致每天超过 1000000 个 sql 请求。所以我想我肯定需要将类别拉到一个数组中,然后递归地遍历数组。

我已经查看了上面的解决方案,它们似乎在纸上有效,但在实践中却没有。

如果有人可以尝试给我另一种解决方案,将不胜感激..

只是回顾一下我的数据库:id,category_name,parent_id

我现在正在使用 PDO 和 mysql 并准备语句以增加安全性..

提前致谢..

4

6 回答 6

3

这是您需要的代码

category_id AS id ,类别标题 AS categori 和 parent_id AS Kid

function countsubcat($pid)
{

   $r=mysql_query("select count(kid) AS say from meskat where kid='$pid' limit 1");

   $rw=mysql_fetch_array($r);

   return $rw['say'];

}

function listmenu($pid = 0)
{

   $res = mysql_query("select id,kategori,kid from meskat where kid='$pid'");

   while($cat=mysql_fetch_array($res))

   {

     echo '<li>';

     print'<a href="#">'.$cat['kategori'].'</a>';

     if(countsubcat($cat['id'])>0)

     {

      print'<ul>';

         listmenu($cat['id']);

      print'</ul>';

     }
   echo '</li>';

   }

}

echo '<ul>';

listmenu(0); //starting from base category

echo '</ul>';`
于 2012-09-26T18:32:53.403 回答
1

在 MySQL 中,这将需要许多查询,每个级别一个,至少一个。

  1. 您获取所有顶级类别,将它们用作森林(一组树)的根节点。
  2. 您获取属于其中任何一个子级的所有类别。
  3. 将这些类别中的每一个作为子节点添加到父节点
  4. 如果还有孩子,则进行第二步,否则停止。

你最终会得到一棵树,你首先深度遍历它以转换为一些(大概)html 表示(例如嵌套列表)。

您可以执行一些优化。如果您按父 ID 对子级别进行排序,那么您可以假设子级别是连续的,这意味着您不必进行尽可能多的查找来找到正确的父级,只需检查父级 ID 的变化。

如果您维护一个由 ID 索引的当前最低级别类别的列表,则可以加快林的创建速度。

如果您重叠第 2 步和第 4 步,那么您只执行 n+1 个查询(其中 n 是级别数),因此检查更多子级也是抓取下一级子级。

在内存方面,这将使用树所需的内存,加上最低级别的查找表和当前父 ID。

构建算法相当快,随着类别的数量线性缩放。

此方法还成功地避免了损坏的数据部分,因为它不会抓取具有循环(无根节点)的数据。

我还建议对子查询使用准备好的语句,因为 MySQL 将编译和缓存查询,它将通过仅通过网络发送新数据来加快操作。

如果您不理解某些概念,我建议您访问 Wikipedia,因为我已尝试对这些概念使用标准术语。

于 2012-07-23T23:38:23.910 回答
0

因此,在对不同解决方案进行了各种尝试之后,我实际上已经使用 jquery 和 ajax 加载来检索下一个级别。

我的主要类别都有 1 的父级 - 商店我对所有以 1 作为父级 ID 的类别进行 mysql 扫描。

这将显示主要类别。附加到每个主要类别,如果它们有任何子类别,我添加了一个文件夹图标。我还插入了一个带有该类别 id 的空白 div。

单击该文件夹时,它会对 php 脚本进行 ajax 调用,以查找具有该父级的所有类别。这些附加到 parents_id div。

这又添加了类别(使用 css 填充以指示子类别),如果它具有子类别,则再次添加一个文件夹符号以及另一个具有 category_id 的空白 div。

您可以根据客户的需要对尽可能多的类别和子类别继续执行此操作。

我更进一步,为类别/子类别添加了一个编辑图标,以便可以移动或更改类别。

如果您想要一些示例代码,请告诉我...

于 2012-08-25T12:00:43.513 回答
0

如果您可以假设任何类别category_id > parent_id为真,则以下递归函数将菜单表示为多级数组并将其呈现为嵌套 HTML 列表将起作用:

$menu = array();

$query = mysql_query("SELECT category_id, category_title, parent_id FROM menu ORDER BY category_id ASC");
if(mysql_num_rows($query) != 0) {
        while($row = mysql_fetch_assoc($query)) {
            if(is_null($row['parent_id']))
                $menu['children'][] = $row;
            else
                add_to_menu(&$menu,$row);
        }
}

function add_to_menu($menu,$item) {
    if(isset($menu['children'])) {
        foreach($menu['children'] as &$child) {
            if($item['parent_id'] == $child['category_id']) {
                $child['children'][] = $item;
            } else {
                add_to_menu(&$child,$item);
            }
        }
    }
}

function render_menu($menu) {
    if(isset($menu['children'])) {
        echo '<ul>';
        foreach($menu['children'] as &$child) {
            echo "<li>";
            echo $child['category_id']." : ".$child['category_title'];
            if(isset($child['children'])) {
                render_menu(&$child);
            }
            echo "</li>";
        }
        echo '</ul>';
    }
}

render_menu($menu);
于 2012-07-24T00:01:28.847 回答
0

你能改变表结构吗?在这种情况下,您可以查看嵌套集模型(链接包括描述和实现细节,向下滚动到嵌套集模型)。节点插入和删除变得更加复杂,但允许您在一次查询中检索整个树。

于 2012-07-24T00:10:07.553 回答
0

实际上,您需要很少的代码来构建一棵树。您也只需要一个查询。这是我想出的(我使用 Drupal 的数据库,但它足够明确,可以理解):

$items = db_select('menu_items', 'mi')
    ->fields('mi')
    ->orderBy('position')
    ->execute()
    ->fetchAllAssoc('id', PDO::FETCH_ASSOC);

// Example of result. The ID as key is important for this to work.
$items = array(
    3 => array('id' => 3, 'parent' => NULL, 'title' => 'Root', 'position' => 0),
    4 => array('id' => 4, 'parent' =>    3, 'title' => 'Sub',  'position' => 0),
    5 => array('id' => 5, 'parent' =>    4, 'title' => 'Sub sub', 'position' => 0),
    6 => array('id' => 6, 'parent' =>    4, 'title' => 'Sub sub', 'position' => 1),
);

// Create the nested structure. Note the & in front of $item.
foreach($items as &$item)
    if($item['parent'])
        $items[$item['parent']]['sub items'][$item['mid']] =& $item;

// Now remove the children from the root
foreach($items as $id => $item)
    if($item['parent']) // This is a child
        unset($items[$id])

此时,您只需要一个递归函数来显示菜单:

function print_menu($items) {
    echo '<ul>';
    foreach($items as $item) {
        echo '<li>';

        echo '<a href="#">' . $item['title'] . '</a>';
        if(!empty($item['sub items']))
                print_menu($item['sub items']);

        echo '</li>';
    }
    echo '</ul>';
}
于 2013-07-13T11:58:57.740 回答