Php 如何显示类别和子类别?

Php 如何显示类别和子类别?,php,mysql,hierarchical-data,Php,Mysql,Hierarchical Data,如何显示类别和子类别? 我有一张DB格式的桌子。此表中的行如下所示: CREATE TABLE IF NOT EXISTS `category` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(45) NOT NULL, `parent_id` int(11) NOT NULL, `order` int(11) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM <?p

如何显示类别和子类别? 我有一张DB格式的桌子。此表中的行如下所示:

CREATE TABLE IF NOT EXISTS `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `parent_id` int(11) NOT NULL,
  `order` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM
<?php 
include 'Category_model.php'; 
include 'Advert_model.php'; 

$pdo = new PDO('mysql:host=localhost;dbname=advert', 'root', ''); 
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$nr = $_GET['id'];

$sql = 'SELECT * FROM category where `parent_id` = :id or `id` = :id';
$stmt = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$stmt->execute(array(':id' => $nr));
$menuCategories = $sth->fetchAll();

// If sub category id is passed fetch whole menu
if ((1 == count($menuCategories)) && ($nr == $menuCategories[0]['id'])) {
    $nr = $menuCategories[0]['parent_id'];
    $stmt->execute(array(':id' => $nr));
    $menuCategories = $sth->fetchAll();
}

$parentCategoryFormat = '<h5><a href="category_view.php?id=%s">%s</a></h5>';
$subCategoryFormat = '<li><a href="category_view.php?id=%s">%s</a></li>';
$parentCategoryHTML = '';
$subMenuHTML = '';
foreach ($menuCategories as $row) {
    if ($row['id'] == $nr) {
        $parentCategoryHTML = sprintf($parentCategoryFormat, $row['id'], htmlentities($row['name'])); // Render parent category
    } else {
        $subMenuHTML .= sprintf($subCategoryFormat, $row['id'], htmlentities($row['name'])); // Render subcategory
    }
 }

 echo $parentCategoryHTML;
 if (!empty($subMenuHTML)) {
      echo "<ul>{$subMenuHTML}</ul>";
 }
我想在这个网站上显示类别和子类别:(左边的菜单)

我的尝试:

<?php 
include 'Category_model.php'; 
include 'Advert_model.php'; 
$nr = $_GET['id']; 

function show_category($nr){ 


    try 
        { 
            $pdo = new PDO('mysql:host=localhost;dbname=advert', 'root', ''); 
            $pdo -> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); 

            $stmt = $pdo -> query("SELECT * FROM category where `parent_id` = $nr"); 


            echo "<ul>";  

            foreach ($stmt as $row){ 
                echo "<li><a href=category_view.php?id={$row['id']}> {$row['name']}</a>".show_category($row['id'])."</li>"; 

                } 
                echo "</ul>"; 
                //$id = $_GET['id']; 
                } 






       catch(PDOException $e) 
                { 
           echo 'Error!: ' . $e->getMessage(); 

           } 

   } 

   show_category($nr); 

对于这样的结构,您需要首先为所选项目构造一个查询,然后为所选项目父级构造一个查询。从本质上讲,您将需要对每一级菜单进行查询,这些菜单应该是可见的


这就是为什么树关系的父id方案不是很有效的原因。最好使用嵌套集(http://en.wikipedia.org/wiki/Nested_set_model)或物化路径(http://en.wikipedia.org/wiki/Materialized_path). 如果您希望对项目进行一些更新,那么从长远来看,物化路径可能更容易处理-我个人更喜欢它,已经尝试了这两种方法。

下面的代码应该可以帮助您开始。这是一个“递归函数”——一个调用自身的函数。正如@Fake51刚刚提到的,它的效率不是很高,但应该可以工作

您将需要一些CSS来构造您生成的列表

function showItems($parent = 0) {
    $q = "SELECT id, name FROM category WHERE parent_id = $parent";
    $q = mysql_query($q);
    if(mysql_num_rows($q)) {
        echo "<ul>";
        while($r = mysql_fetch_row($q)) {
            echo "<li>";
            echo "<a href=\"page.php?id=".$r[0]."\">".htmlentities($r[1])."</a>";
            showItems($r[0]);
            echo "</li>"\n;
        }
        echo "</ul>\n";
    }
}
showItems();
函数showItems($parent=0){
$q=“从父类中选择id、名称,其中父类\u id=$parent”;
$q=mysql\u查询($q);
if(mysql_num_行($q)){
回声“
    ”; 而($r=mysql\u fetch\u row($q)){ 回声“
  • ”; 回声“; 展示项目($r[0]); 回声“
  • ”\n; } 回声“
\n”; } } showItems();
编辑:由于仍然没有被接受的答案,这里是我的代码修改,用一个SQL查询来完成这一切,这应该会更有效,尽管可能会有点混乱。看看你过得怎么样

//Recursive function to show menu items from a passed in array
function showItems(&$menu, $parent = 0) {
    if(is_array($menu[$parent]) && sizeof($menu[$parent])) {
        echo "<ul>";
        foreach($menu[$parent] as $num=>$name) {
            echo "<li>";
            echo "<a href=\"page.php?id=".$num."\">".htmlentities($name)."</a>";
            showItems($menu, $num);
            echo "</li>\n";
        }
        echo "</ul>\n";
    }
}

//Create a multi-dimensional array of ALL menu items, separated by parent
$menu = array();
$q = "SELECT id, name, parent_id FROM category ORDER BY order";
$q = mysql_query($q);
while($r = mysql_fetch_row($q)) {
    $menu[$r[2]][$r[0]] = $r[1];
}

//Call the function
showItems($menu);
//递归函数,用于显示传入数组中的菜单项
函数showItems(&$menu,$parent=0){
if(is_数组($menu[$parent])&&sizeof($menu[$parent])){
回声“
    ”; foreach($menu[$parent]作为$num=>$name){ 回声“
  • ”; 回声“; showItems($menu$num); 回声“
  • \n”; } 回声“
\n”; } } //创建由父菜单项分隔的所有菜单项的多维数组 $menu=array(); $q=“从类别顺序中逐个顺序选择id、名称、父\u id”; $q=mysql\u查询($q); 而($r=mysql\u fetch\u row($q)){ $menu[$r[2]][$r[0]]=$r[1]; } //调用函数 展示项目(菜单);
因为您只需要显示类别及其所有子类别,所以构建菜单时不需要递归,当然也不希望在DB调用中使用递归。所以最基本的解决方案(所有代码都在一个文件中)是这样的:

CREATE TABLE IF NOT EXISTS `category` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(45) NOT NULL,
  `parent_id` int(11) NOT NULL,
  `order` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM
<?php 
include 'Category_model.php'; 
include 'Advert_model.php'; 

$pdo = new PDO('mysql:host=localhost;dbname=advert', 'root', ''); 
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);

$nr = $_GET['id'];

$sql = 'SELECT * FROM category where `parent_id` = :id or `id` = :id';
$stmt = $pdo->prepare($sql, array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY));
$stmt->execute(array(':id' => $nr));
$menuCategories = $sth->fetchAll();

// If sub category id is passed fetch whole menu
if ((1 == count($menuCategories)) && ($nr == $menuCategories[0]['id'])) {
    $nr = $menuCategories[0]['parent_id'];
    $stmt->execute(array(':id' => $nr));
    $menuCategories = $sth->fetchAll();
}

$parentCategoryFormat = '<h5><a href="category_view.php?id=%s">%s</a></h5>';
$subCategoryFormat = '<li><a href="category_view.php?id=%s">%s</a></li>';
$parentCategoryHTML = '';
$subMenuHTML = '';
foreach ($menuCategories as $row) {
    if ($row['id'] == $nr) {
        $parentCategoryHTML = sprintf($parentCategoryFormat, $row['id'], htmlentities($row['name'])); // Render parent category
    } else {
        $subMenuHTML .= sprintf($subCategoryFormat, $row['id'], htmlentities($row['name'])); // Render subcategory
    }
 }

 echo $parentCategoryHTML;
 if (!empty($subMenuHTML)) {
      echo "<ul>{$subMenuHTML}</ul>";
 }

我不知道该怎么办。请执行“算法”/“步骤如何”您首先需要使用
mysql\u connect()
连接,然后使用
mysql\u select\u db()选择一个数据库。如果您愿意,您应该能够将其转换为PDO。这太糟糕了。我更愿意做一个单一的选择,得到所有类别的数组,然后使用这个数组。此外,如果您可以将另一个字段添加到类别表“has_children”中并存储值(如yes/no或boolean),这将允许您使用更高效的算法。结果应在单个查询中获取,然后由构建菜单的递归函数处理。请同意重复查询是次优的。构建一组数组的单个查询将是一种更好的方法。某种形式的缓存也是一个明智的想法,因为它可能不太可能经常更改。我选择了这个,因为它很容易开始,也许应该建议这个改变。一点也不正确。您可以选择所有行,理想情况下按id对它们进行索引,然后遍历并生成结果。这是一种资源浪费,因为您不需要所有结果-可能隐藏了大量不需要详细信息的子类别项。除此之外,如果你有一个合适的结构,在php中遍历和构建结果将比在数据库中遍历和构建结果慢。当你点击一个子类别时,$nr将是子类别的id,因此只获取一行,而不是整个可见树。更不用说sql中的失败引用以及代码中的sql expoit向量了。@Fake51注意到了,谢谢。我更新了代码示例。我可能应该指出,即使更新后的代码也不是产品质量,我只是想用这段代码说明,在这种特定情况下,如果需要更复杂(更深)的菜单结构,就不需要递归,特别是使用DB查询的递归,更好的解决方案是更改“category”DB表结构,就像您在回答中建议的那样。