如何使用 PHP 中的嵌套函数从 selected 类别中 select 子类别?
How to select subcategories from selected category using a nested function in PHP?
首先,我 post 对此提出了另一个问题,但没有得到答案,所以我需要 post 一个新问题。
我有一个按层次列出类别的函数,我创建了另一个函数(在同一个函数上),其中包含 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;
}
用法:echo listCategoryTree(0, $categoryMulti);
下面是我在下拉列表中的使用方式,仅作为示例,效果很好:
function selectCategories($categories)
{
foreach ($categories as $category) {
echo '<option value="' . $category['cat_id'] . '">' . $category['cat_name'] . '</option>';
}
}
selectCategories($allCategories);
这是我尝试使其与所选类别方法一起使用的示例之一,但不幸的是,它没有用。
我在我的函数中创建了一个新数组:
$subCategories = array();
$subCategories[] = $row;
并将其作为函数调用,但没有用。
function cats($categories)
{
foreach($categories as $category) {
echo '<ul>';
echo '<li>' . $category['cat_name'] . '</li>';
echo '</ul>';
}
}
echo cats(100, $subCategories);
有几个解决方案。首先,我以下面的数据(categories
table)为例。
+----+--------------------------+-----------+
| 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 |
+----+--------------------------+-----------+
解决方案 1(Adjacency List):
您可以使用 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);
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();
}
如果您使用的是 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();
}
获取数据库中的所有类别:
$allCategories = getCategories($pdo);
输出:
+----+--------------------------+-----------+
| 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 |
+----+--------------------------+-----------+
获取类别的子类别:
$subCategories = getCategories($pdo, 1); // 1 is parent_id
输出:
+----+--------------------------+-----------+
| 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 |
+----+--------------------------+-----------+
如果你想要一个 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;
}
echo buildCategories(prepareCategories($allCategories));
输出:
echo buildCategories(prepareCategories($subCategories), 1);
输出:
解决方案 2(Nested Sets):
我们将向我们的 table 添加额外的列 left
和 right
并在其中添加数字以标识属于父组的组。 (请注意,我们不会使用 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 |
+----+--------------------------+--------------------------+
现在,我们需要更改函数:
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();
}
获取数据库中的所有类别:
$allCategories = getCategories($pdo);
输出:
+----+--------------------------+--------------------------+
| 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 |
+----+--------------------------+--------------------------+
获取类别的子类别:
$subCategories = getCategories($pdo, 1); // 1 is parent_id
输出:
+----+--------------------------+--------------------------+
| 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 |
+----+--------------------------+--------------------------+
您可以按照解决方案 1 中所示呈现 HTML。 Read more 关于在嵌套集模型中更新和插入新数据。
来源和阅读:
首先,我 post 对此提出了另一个问题,但没有得到答案,所以我需要 post 一个新问题。
我有一个按层次列出类别的函数,我创建了另一个函数(在同一个函数上),其中包含 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;
}
用法:echo listCategoryTree(0, $categoryMulti);
下面是我在下拉列表中的使用方式,仅作为示例,效果很好:
function selectCategories($categories)
{
foreach ($categories as $category) {
echo '<option value="' . $category['cat_id'] . '">' . $category['cat_name'] . '</option>';
}
}
selectCategories($allCategories);
这是我尝试使其与所选类别方法一起使用的示例之一,但不幸的是,它没有用。
我在我的函数中创建了一个新数组:
$subCategories = array();
$subCategories[] = $row;
并将其作为函数调用,但没有用。
function cats($categories)
{
foreach($categories as $category) {
echo '<ul>';
echo '<li>' . $category['cat_name'] . '</li>';
echo '</ul>';
}
}
echo cats(100, $subCategories);
有几个解决方案。首先,我以下面的数据(categories
table)为例。
+----+--------------------------+-----------+
| 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 |
+----+--------------------------+-----------+
解决方案 1(Adjacency List):
您可以使用 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);
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();
}
如果您使用的是 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();
}
获取数据库中的所有类别:
$allCategories = getCategories($pdo);
输出:
+----+--------------------------+-----------+
| 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 |
+----+--------------------------+-----------+
获取类别的子类别:
$subCategories = getCategories($pdo, 1); // 1 is parent_id
输出:
+----+--------------------------+-----------+
| 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 |
+----+--------------------------+-----------+
如果你想要一个 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;
}
echo buildCategories(prepareCategories($allCategories));
输出:
echo buildCategories(prepareCategories($subCategories), 1);
输出:
解决方案 2(Nested Sets):
我们将向我们的 table 添加额外的列 left
和 right
并在其中添加数字以标识属于父组的组。 (请注意,我们不会使用 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 |
+----+--------------------------+--------------------------+
现在,我们需要更改函数:
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();
}
获取数据库中的所有类别:
$allCategories = getCategories($pdo);
输出:
+----+--------------------------+--------------------------+
| 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 |
+----+--------------------------+--------------------------+
获取类别的子类别:
$subCategories = getCategories($pdo, 1); // 1 is parent_id
输出:
+----+--------------------------+--------------------------+
| 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 |
+----+--------------------------+--------------------------+
您可以按照解决方案 1 中所示呈现 HTML。 Read more 关于在嵌套集模型中更新和插入新数据。
来源和阅读: