html <select>多个级别</select>

时间:2015-04-02 16:25:29

标签: php tree

我试图在<select>元素中实现以下输出。

Mouses
  Logitech
    Red
    White
  Microsoft
    Black

Keyboards
  Logitech
    Small size
       Red
       Green
    Medium size
      Black

正如您所看到的,可能存在不定级别。 在我的MySQL数据库中,我创建了一个表类别

id
description
category_parent_id

输入非常简单。

1   Mouses      null
2   Logitech     1
3   Red          2
4   White        2
5   Keyboards    null
6   Logitech     5

等等。正如您所看到的那样,第一个类别被设置为null以识别原点。

// Disables duplicated categories
$alreadyOutput = array();

foreach($categories as $category)
{
    if(in_array($category->id, $alreadyOutput) == FALSE)
    {
        echo '<option value="' . $category->id . '">';
        echo $category->description;
        echo '</option>';
    }

    foreach($categories as $subCategory)
    {
        if($subCategory->category_parent_id == $category->id &&
           in_array($subCategory->id, $alreadyOutput) == FALSE)
        {
            echo '<option value="' . $subCategory->id . '">';
            echo '&nbsp;&nbsp;&nbsp;' . $subCategory->description;
            echo '</option>';
        }
    }
}

使用此代码我的输出是:

Mouses
  Logitech
  Red
  White
  Microsoft
  Black

Keyboards
  Logitech
  Small size
  Red
  Green
  Medium size
  Black

我明白为什么。 首先,空格&nbsp&nbsp&nbsp只进行一次,并应根据级别递增。我试图通过创建两个函数来提出解决方案。第一个确定了为达到原点而需要执行的步骤数量。

function stepsToJump($categories, $id, $category_parent_id)
{           
    $parentPosition  = 0;
    $position        = 0;

    foreach($categories as $i => $category)
    {
        if($category->id == $category_parent_id)
            $parentPosition = $i;

        if($category->id == $id)
            $position = $i;
    }

    return $position - $parentPosition;
}

function htmlSpaces($amount)
{
    $html = '&nbsp;&nbsp;&nbsp;'; // default

    for($i = 0; $i < $amount; $i++)
        $html .= '&nbsp;&nbsp;&nbsp;';

    return $html;
}

有了这个,我就用这种方式实现了:

echo htmlSpaces(stepsToJump($categories, 
                            $subCategory->id, 
                            $category->id)) . $subCategory->description;

不幸的是我的输出错了。

Mouses
  Logitech
    Red
        White
  Microsoft
    Black

Keyboards
  Logitech
    Small size
        Red
            Green
    Medium size
        Black

1 个答案:

答案 0 :(得分:0)

我发现从头开始编写比调试代码更容易。以下代码按预期工作。

$dbh = new PDO('---connection string---');

$stmt_categories = $dbh->prepare("SELECT * FROM categories");
$stmt_categories->execute();
$categories = $stmt_categories->fetchAll(PDO::FETCH_ASSOC);
$allCategories = $categories;

$lastParent = NULL; // we will use this variable to place categories under their parents.

print '<select>';

while (count($categories) > 0) { // we use this control, because we will unset elements from $categories. until $categories array is empty, following foreach loop will run continously.

    foreach ($categories as $i => $category) {

        if ($category['category_parent_id'] == $lastParent) { // by using this control, we will be sure output starts with a top level category.

            if ($category['category_parent_id'] == NULL) {
                $indent = 0; // since this is a top level category, indent is 0.
            }

            print '<option value="' . $category['id'] . '">';
            print str_repeat('&nbsp;', ($indent)) . $category['description'];
            print '</option>';

            if (hasChildren($categories, $category['id']) == TRUE) {

                $lastParent = $category['id'];
                $indent++; // this is a parent category. so, one of its children is coming on the next iteration. we incremented $indent to be inherited by the child.

            }

            unset($categories[$i]);

        } else {

            // ok, this category is not a child of our $lastParent. if all the children of $lastParent are unset from $categories array, this loop will continue forever. so, we are going to control if there are other children of $lastParent.

            if (hasChildren($categories, $lastParent) == TRUE) {

                continue; // there is still at least one child of $lastParent. it is ok to continue to next iteration.

            } else {

                // there are no (more) children of $lastParent. so, we need to manipulate this variable. let's check parent of $lastParent on the next iteration.

                $lastParent = parentCategory($allCategories, $lastParent);
                $indent--;

            }

        }

    }

}

print '</select>';


function hasChildren($categories, $categoryID) {

    foreach($categories as $i => $category) {

        if ($category['category_parent_id'] === $categoryID) {

            return TRUE;

        }

    }

    return FALSE;

}

function parentCategory($categories, $categoryID) {

    foreach ($categories as $i => $category) {

        if ($category['id'] === $categoryID) {
            return $category['category_parent_id'];
        }

    }

}