简体   繁体   English

如何使用PHP和SQL创建“多维树菜单”?

[英]How to create “multidimensional tree menu” using PHP and SQL?

I searched for awhile, but couldn't find anything on google and other forums. 我搜索了一段时间,但在Google和其他论坛上找不到任何内容。 But it's weird because it's popular task in our university. 但这很奇怪,因为它在我们大学中很受欢迎。 So I think this post might help other too with same issue. 因此,我认为这篇文章也可能对其他问题有所帮助。

I got a task: 我有一个任务:

"Create multidimensional tree menu (depth is undefined, could be 4 or 7, depending on user's will), there should be options like adding new element, editing, deleting and showing the whole tree structure. Using PHP, SQL and minimal CSS." “创建多维树菜单(深度是不确定的,可以是4或7,具体取决于用户的意愿),应该有添加新元素,编辑,删除和显示整个树结构的选项。使用PHP,SQL和最少的CSS。”

Visual example of a tree: 树的可视示例:

+Menu
    +Shop
        +Tshirts
            +Yellow
            +Green
        +Pants
    +Forum
        +Ideas

As you can see it's 4 depth levels: Menu->Shop->tshirt->yellow But I have to make it so that user could add as many levels as he wants and elements to it. 如您所见,它有4个深度级别: Menu->Shop->tshirt->yellow但是我必须做到这一点,以便用户可以根据需要添加任意多个级别。

Is there any examples to it and what SQL structure should I keep on? 是否有任何示例,我应该保留哪种SQL结构? Thank you! 谢谢!

You want to save each element in the DB with an ID and a parentID (that can be null if no such parent exists). 您想要使用ID和parentID(如果不存在这样的父级,则可以为null)将每个元素保存在数据库中。 The PHP is your "biggest" problem, but references are your huge friend here for turning a flat structure into a tree structure. PHP是您的“最大”问题,但是在这里,引用是将平面结构转换为树形结构的好朋友。

Consider the following DB result: 考虑以下数据库结果:

----------------------------
| id | parentID | text     |
|----|----------|----------|
| 1  | null     | Item #1  |
| 2  | 5        | Item #2  |
| 3  | 2        | Item #3  |
| 4  | 2        | Item #4  |
| 5  | null     | Item #5  |
| 6  | 5        | Item #6  |
| 7  | 3        | Item #7  |
| 8  | 5        | Item #8  |
| 9  | 1        | Item #9  |
| 10 | 7        | Item #10 |
----------------------------

Consider the following array (that could be from a DB result - it's important that the ID is the key, though. You can simply transform your DB result into something like the following (the only needed key is "parentID"): 考虑以下数组(可能来自数据库结果-尽管ID是关键,这一点很重要。您可以将数据库结果简单地转换为以下内容(唯一需要的键是“ parentID”):

$menu = array(
    1 => array('text' => 'Item #1', 'parentID' => null),
    2 => array('text' => 'Item #2', 'parentID' => 5),
    3 => array('text' => 'Item #3', 'parentID' => 2),
    4 => array('text' => 'Item #4', 'parentID' => 2),
    5 => array('text' => 'Item #5', 'parentID' => null),
    6 => array('text' => 'Item #6', 'parentID' => 5),
    7 => array('text' => 'Item #7', 'parentID' => 3),
    8 => array('text' => 'Item #8', 'parentID' => 5),
    9 => array('text' => 'Item #9', 'parentID' => 1),
   10 => array('text' => 'Item #10', 'parentID' => 7),
);

And to turn it into a tree structure: 并将其变成树形结构:

<?php    
$addedAsChildren = array();

foreach ($menu as $id => &$menuItem) { // note that we use a reference so we don't duplicate the array
    if (!empty($menuItem['parentID'])) {
        $addedAsChildren[] = $id; // it should be removed from root, but we'll do that later

        if (!isset($menu[$menuItem['parentID']]['children'])) {
            $menu[$menuItem['parentID']]['children'] = array($id => &$menuItem); // & means we use the REFERENCE
        } else {
            $menu[$menuItem['parentID']]['children'][$id] = &$menuItem; // & means we use the REFERENCE
        }
    }

    unset($menuItem['parentID']); // we don't need parentID any more
}

unset($menuItem); // unset the reference

foreach ($addedAsChildren as $itemID) {
    unset($menu[$itemID]); // remove it from root so it's only in the ['children'] subarray
}

With this new array we can use a simply recursive function to output it all in a ul..li sense: 有了这个新数组,我们可以使用简单的递归函数以ul..li输出所有ul..li

echo makeTree($menu);

function makeTree($menu) {
    $tree = '<ul>';

    foreach ($menu as $id => $menuItem) {
        $tree .= '<li>' . $menuItem['text'];

        if (!empty($menuItem['children'])) {
            $tree .= makeTree($menuItem['children']);
        }

        $tree .= '</li>';
    }

    return $tree . '</ul>';
}

Resulting in: 导致:

<ul><li>Item #1<ul><li>Item #9</li></ul></li><li>Item #5<ul><li>Item #2<ul><li>Item #3<ul><li>Item #7<ul><li>Item #10</li></ul></li></ul></li><li>Item #4</li></ul></li><li>Item #6</li><li>Item #8</li></ul></li></ul>

..and rendered: ..渲染:

在此处输入图片说明在此处输入图片说明

DEMO 演示

I would suggest the following structure for the MySQL: 我会为MySQL建议以下结构:

I_ID (unique, auto-increment), Item Name, Item Parent ID, Children

The "Children" would probably contain a base64-encoded, serialized PHP array of the I_IDs of each 'child' of that specific item. “子项”可能包含该特定项目的每个“子项”的I_ID的base64编码的序列化PHP数组。

The root element would have a "parent ID" of "root" or "-1" or something, so to retrieve this out of the database you'd first SELECT anything that had that parent of root or -1 or whatever you choose. root元素的“父ID”为“ root”或“ -1”或其他内容,因此要从数据库中检索出来,您首先要SELECT具有root或-1 parent的任何东西或您选择的任何东西。 It's arbitrary. 这是任意的。

Then, with the PHP, you would decode the children array, and SELECT each of those IDs from the system. 然后,使用PHP,您将解码children数组,并从系统中选择每个ID。

Repeat until there are no more children. 重复直到没有更多孩子为止。 Recursive functions will serve you well here. 递归函数将在这里很好地为您服务。

When you add each row into the DOM, give them a specific class that makes them hidden. 当您将每行添加到DOM中时,为它们提供一个特定的类使其隐藏。 When the "+" is clicked, use javascript to change all of that element's children to have a class that is not hidden, and replaces the just-clicked "+" button with a "-" button. 单击“ +”时,请使用javascript更改该元素的所有子级,使其具有未隐藏的类,然后将单击的“ +”按钮替换为“-”按钮。 Handle the minus button similarly. 类似地处理减号按钮。

The solution is quite simple: 解决方案非常简单:

SQL structure table menu_entries (int id, varchar title, varchar link, int level, bool is_parent) SQL结构表menu_entries(int id,varchar标题,varchar链接,int级别,bool is_parent)

You can add options like additional links, colors and etc. 您可以添加其他链接,颜色等选项。

default level is 1. default is_parent = false; 默认级别为1。默认is_parent = false;

After that proceed to get your entries into the database. 之后,继续将您的条目输入数据库。 If item has subitems - is_parent = true, and all subitems have level +1 of parent's level. 如果item有子项-is_parent = true,并且所有子项都具有父级的+1级。

In outputting just add formating that any new "level" items are indented. 在输出中,只需添加格式,即缩进任何新的“级别”项。 Also, if item's link is current URL of the page - highlight the item as active. 另外,如果项目的链接是页面的当前URL,则突出显示该项目为活动状态。

That's all. 就这样。

Here's code from my link directory script's category table which does what you're looking for: 这是我的链接目录脚本的类别表中的代码,它可以满足您的需求:

DB Design: 数据库设计:

CREATE TABLE IF NOT EXISTS `categories` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(36) COLLATE utf8_unicode_ci NOT NULL,
  `parent` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

To print them out using PHP in dashes: 要使用PHP破折号打印出来:

function parse($tree,$parent = 0,$level = 0){
    $level++;
    foreach ($tree as $p)  {
        if ($p['parent'] != $parent) continue;
        $p['name'] = str_repeat('-',$level-1) . $p['name'];
        echo "<option name='cat' value='{$p['id']}'>{$p['name']}</option>";
        parse ($tree, $p['id'],$level);
    }
}

    $q = $link->query("SELECT * FROM `categories` ORDER BY name ASC");
    $cats = array();
    while($row = $q->fetch()){
        $cats[] = array("id"=>$row['id'],"name"=>$row['name'],"parent"=>$row['parent'],"level"=>0);
    }

parse($cats)

You can remove the $level param if you use 您可以删除$ level参数(如果使用)

  • instead of number of dashes for output. 而不是用于输出的破折号。

  • 声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

     
    粤ICP备18138465号  © 2020-2024 STACKOOM.COM