如何使用 PHP 中的嵌套函数从所选类别中选择子类别?

Dlk*_*Dlk 2 php mysql hierarchical-data

首先,我发布了另一个关于此的问题,但没有得到答案,所以我需要发布一个新问题。

我有一个按层次列出类别的函数,我用foreach循环创建了另一个函数(在同一个函数上),并将其用于下拉列表,所以不需要创建另一个查询,多次使用我现有的函数。

我想使用相同的查询来列出所选类别下的子类别。

示例:如果category.php?id=100单击,那么我想在登录页面中列出 100 层级下的子类别。

这是我的功能:

$allCategories = array();
$categoryMulti = array(
    'categories' => array(),
    'parent_cats' => array()
);
while ($row = $stmt->fetch()) {
//I created another array to get subcats didnt work
//$categories['categories'][1]
    $categoryMulti['categories'][$row['cat_id']] = $row;
    $categoryMulti['parent_cats'][$row['parent_id']][] = $row['cat_id'];
    $allCategories[] = $row;
}
function listCategoryTree($parent, $category)
{
    $html = "";
    if (isset($category['parent_cats'][$parent])) {
        $html .= "<ul>\n";
        foreach

 ($category['parent_cats'][$parent] as $cat_id) {
                if (!isset($category['parent_cats'][$cat_id])) {
                    $html .= "<li>" . $category['categories'][$cat_id]['cat_name'] . "</li>";
                } else {
                    $html .= "<li>" . $category['categories'][$cat_id]['cat_name'];
                    $html .= listCategoryTree($cat_id, $category);
                    $html .= "</li>";
                }
            }
            $html .= "</ul> \n";
        }
        return $html;
    }
Run Code Online (Sandbox Code Playgroud)

用法 : echo listCategoryTree(0, $categoryMulti);

这是我在下拉列表中使用的方式,仅作为示例,它工作正常:

function selectCategories($categories)
{
    foreach ($categories as $category) {
        echo '<option value="' . $category['cat_id'] . '">' . $category['cat_name'] . '</option>';
    }
}
selectCategories($allCategories);
Run Code Online (Sandbox Code Playgroud)

这是我试图使其与所选类别方法一起工作的示例之一,但不幸的是,它没有奏效。

我在我的函数中创建了一个新数组:

$subCategories = array();
$subCategories[] = $row;
Run Code Online (Sandbox Code Playgroud)

并将其称为类似以下但不起作用的函数。

function cats($categories)
{
    foreach($categories as $category) {
        echo '<ul>';
        echo '<li>' . $category['cat_name'] . '</li>';
        echo '</ul>';
    }
}

echo cats(100, $subCategories);
Run Code Online (Sandbox Code Playgroud)

N'B*_*yev 7

有几种解决方案。首先,我将以下面的数据(categories表)为例。

+----+--------------------------+-----------+
| id | name                     | parent_id |
+----+--------------------------+-----------+
|  1 | Electronics              |      NULL |
|  2 | Apparel & Clothing       |      NULL |
|  3 | Phones & Accessories     |         1 |
|  4 | Computer & Office        |         1 |
|  5 | Men's Clothing           |         2 |
|  6 | Women's Clothing         |         2 |
|  7 | Cell Phones              |         3 |
|  8 | Cell Phone Accessories   |         3 |
|  9 | Phone Parts              |         3 |
| 10 | Computers & Accessories  |         4 |
| 11 | Tablets & Accessories    |         4 |
| 12 | Computer Peripherals     |         4 |
| 13 | Computer Components      |         4 |
| 14 | Office Electronics       |         4 |
+----+--------------------------+-----------+
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

解决方案 1(邻接列表):

您可以使用WITH(Common Table Expressions)子句(需要 MySQL 8.0)在单个查询中轻松获取某个类别的所有类别或子类别:

// Database connection

$options = [
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
    PDO::ATTR_EMULATE_PREPARES => false,
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
];

$pdo = new PDO('mysql:host=localhost;dbname=<DATABASE_NAME>', '<USERNAME>', '<PASSWORD>', $options);
Run Code Online (Sandbox Code Playgroud)
function getCategories(PDO $db, $parentId = null)
{
    $sql = $parentId ? 'WITH RECURSIVE cte (id, name, parent_id) AS (SELECT id, name, parent_id FROM categories WHERE parent_id = ? UNION ALL SELECT c.id, c.name, c.parent_id FROM categories c INNER JOIN cte ON c.parent_id = cte.id) SELECT * FROM cte' : 'SELECT * FROM categories';
    $stmt = $db->prepare($sql);
    $stmt->execute($parentId ? [$parentId] : null);
    return $stmt->fetchAll();
}
Run Code Online (Sandbox Code Playgroud)

如果您使用的是 MySQL 5.7,请像这样更改该函数:

function getCategories(PDO $db, $parentId = null)
{
    $sql = $parentId ? 'SELECT id, name, parent_id FROM (SELECT * FROM categories ORDER BY parent_id, id) c, (select @pv := ?) initialisation WHERE find_in_set(parent_id, @pv) AND LENGTH(@pv := concat(@pv, ",", id))' : 'SELECT * FROM categories';
    $stmt = $db->prepare($sql);
    $stmt->execute($parentId ? [$parentId] : null);
    return $stmt->fetchAll();
}
Run Code Online (Sandbox Code Playgroud)

要获取数据库中的所有类别:

$allCategories = getCategories($pdo);
Run Code Online (Sandbox Code Playgroud)

输出:

+----+--------------------------+-----------+
| id | name                     | parent_id |
+----+--------------------------+-----------+
|  1 | Electronics              |      NULL |
|  2 | Apparel & Clothing       |      NULL |
|  3 | Phones & Accessories     |         1 |
|  4 | Computer & Office        |         1 |
|  5 | Men's Clothing           |         2 |
|  6 | Women's Clothing         |         2 |
|  7 | Cell Phones              |         3 |
|  8 | Cell Phone Accessories   |         3 |
|  9 | Phone Parts              |         3 |
| 10 | Computers & Accessories  |         4 |
| 11 | Tablets & Accessories    |         4 |
| 12 | Computer Peripherals     |         4 |
| 13 | Computer Components      |         4 |
| 14 | Office Electronics       |         4 |
+----+--------------------------+-----------+
Run Code Online (Sandbox Code Playgroud)

要获取类别的子类别:

$subCategories = getCategories($pdo, 1); // 1 is parent_id
Run Code Online (Sandbox Code Playgroud)

输出:

+----+--------------------------+-----------+
| id | name                     | parent_id |
+----+--------------------------+-----------+
|  3 | Phones & Accessories     |         1 |
|  4 | Computer & Office        |         1 |
|  7 | Cell Phones              |         3 |
|  8 | Cell Phone Accessories   |         3 |
|  9 | Phone Parts              |         3 |
| 10 | Computers & Accessories  |         4 |
| 11 | Tablets & Accessories    |         4 |
| 12 | Computer Peripherals     |         4 |
| 13 | Computer Components      |         4 |
| 14 | Office Electronics       |         4 |
+----+--------------------------+-----------+
Run Code Online (Sandbox Code Playgroud)

如果你想要一个 HTML 输出,你可以遍历$allCategories/ $subCategories(根据你的例子):

function prepareCategories(array $categories)
{
    $result = [
        'all_categories' => [],
        'parent_categories' => []
    ];
    foreach ($categories as $category) {
        $result['all_categories'][$category['id']] = $category;
        $result['parent_categories'][$category['parent_id']][] = $category['id'];
    }
    return $result;
}

function buildCategories($categories, $parentId = null)
{
    if (!isset($categories['parent_categories'][$parentId])) {
        return '';
    }

    $html = '<ul>';
    foreach ($categories['parent_categories'][$parentId] as $cat_id) {
        if (isset($categories['parent_categories'][$cat_id])) {
            $html .= "<li><a href='#'>{$categories['all_categories'][$cat_id]['name']}</a>";
            $html .= buildCategories($categories, $cat_id);
            $html .= '</li>';
        } else {
            $html .= "<li><a href='#'>{$categories['all_categories'][$cat_id]['name']}</a></li>";
        }
    }
    $html .= '</ul>';

    return $html;
}
Run Code Online (Sandbox Code Playgroud)
echo buildCategories(prepareCategories($allCategories));
Run Code Online (Sandbox Code Playgroud)

输出:

在此处输入图片说明

echo buildCategories(prepareCategories($subCategories), 1);
Run Code Online (Sandbox Code Playgroud)

输出:

在此处输入图片说明

解决方案 2(嵌套集):

我们将向我们的表中添加额外的列leftright并在其中输入数字,以标识属于父级的组。(请注意,我们不会使用parent_id列。)

+----+--------------------------+--------------------------+
| id | name                     | parent_id | left | right |
+----+--------------------------+--------------------------+
|  1 | Electronics              |      NULL |    1 |    22 |
|  2 | Apparel & Clothing       |      NULL |   23 |    28 |
|  3 | Phones & Accessories     |         1 |    2 |     9 |
|  4 | Computer & Office        |         1 |   10 |    21 |
|  5 | Men's Clothing           |         2 |   24 |    25 |
|  6 | Women's Clothing         |         2 |   26 |    27 |
|  7 | Cell Phones              |         3 |    3 |     4 |
|  8 | Cell Phone Accessories   |         3 |    5 |     6 |
|  9 | Phone Parts              |         3 |    7 |     8 |
| 10 | Computers & Accessories  |         4 |   11 |    12 |
| 11 | Tablets & Accessories    |         4 |   13 |    14 |
| 12 | Computer Peripherals     |         4 |   15 |    16 |
| 13 | Computer Components      |         4 |   17 |    18 |
| 14 | Office Electronics       |         4 |   19 |    20 |
+----+--------------------------+--------------------------+
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

现在,我们需要改变我们的函数:

function getCategories(PDO $db, $parentId = null)
{
    $sql = $parentId ? 'SELECT children.* FROM categories parent INNER JOIN categories children ON parent.left < children.left AND parent.right > children.left WHERE parent.id = ?' : 'SELECT * FROM categories';
    $stmt = $db->prepare($sql);
    $stmt->execute($parentId ? [$parentId] : null);
    return $stmt->fetchAll();
}
Run Code Online (Sandbox Code Playgroud)

要获取数据库中的所有类别:

$allCategories = getCategories($pdo);
Run Code Online (Sandbox Code Playgroud)

输出:

+----+--------------------------+--------------------------+
| id | name                     | parent_id | left | right |
+----+--------------------------+--------------------------+
|  1 | Electronics              |      NULL |    1 |    22 |
|  2 | Apparel & Clothing       |      NULL |   23 |    28 |
|  3 | Phones & Accessories     |         1 |    2 |     9 |
|  4 | Computer & Office        |         1 |   10 |    21 |
|  5 | Men's Clothing           |         2 |   24 |    25 |
|  6 | Women's Clothing         |         2 |   26 |    27 |
|  7 | Cell Phones              |         3 |    3 |     4 |
|  8 | Cell Phone Accessories   |         3 |    5 |     6 |
|  9 | Phone Parts              |         3 |    7 |     8 |
| 10 | Computers & Accessories  |         4 |   11 |    12 |
| 11 | Tablets & Accessories    |         4 |   13 |    14 |
| 12 | Computer Peripherals     |         4 |   15 |    16 |
| 13 | Computer Components      |         4 |   17 |    18 |
| 14 | Office Electronics       |         4 |   19 |    20 |
+----+--------------------------+--------------------------+
Run Code Online (Sandbox Code Playgroud)

要获取类别的子类别:

$subCategories = getCategories($pdo, 1); // 1 is parent_id
Run Code Online (Sandbox Code Playgroud)

输出:

+----+--------------------------+--------------------------+
| id | name                     | parent_id | left | right |
+----+--------------------------+--------------------------+
|  3 | Phones & Accessories     |         1 |    2 |     9 |
|  4 | Computer & Office        |         1 |   10 |    21 |
|  7 | Cell Phones              |         3 |    3 |     4 |
|  8 | Cell Phone Accessories   |         3 |    5 |     6 |
|  9 | Phone Parts              |         3 |    7 |     8 |
| 10 | Computers & Accessories  |         4 |   11 |    12 |
| 11 | Tablets & Accessories    |         4 |   13 |    14 |
| 12 | Computer Peripherals     |         4 |   15 |    16 |
| 13 | Computer Components      |         4 |   17 |    18 |
| 14 | Office Electronics       |         4 |   19 |    20 |
+----+--------------------------+--------------------------+
Run Code Online (Sandbox Code Playgroud)

您可以按照解决方案 1所示呈现 HTML 。阅读有关在嵌套集模型中更新和插入新数据的更多信息。


来源和阅读:

  • 他们都是假的。但它们的类型并不相同。null 是“NULL”,0 是“整数”。 (2认同)