PHP嵌套foreach循环问题:子分类仅显示在首个父分类下
解决PDO分类循环中子分类仅显示在第一个父分类的问题
嘿,我完全懂你碰到的这个麻烦——所有子分类都挤在第一个父分类下面,后面的父分类压根看不到对应的子分类对吧?你自己排查的结论一点没错:PDOStatement对象在遍历一次之后,内部指针会停在结果集的末尾,后续再去遍历它就拿不到任何数据了。
下面给你几个简单可行的解决办法,挑你觉得顺手的用就行:
方法一:把子分类结果集转成数组(最推荐)
直接把PDO查询到的子分类结果转换成PHP数组,这样每次遍历的都是数组本身,不存在指针位置的问题。改动很小,代码也更直观:
/* Queries */ $categories = $conn->query('SELECT pc.name, pc.ID, COUNT(p.id) AS amount FROM productcategory AS pc INNER JOIN products AS p ON p.categoryID = pc.ID WHERE p.available = 1 GROUP BY pc.ID'); $categories->execute(); // 先把子分类查询结果转成关联数组 $subcategoriesStmt = $conn->query('SELECT pcs.name, pcs.categoryID, pcs.ID FROM productsubcategory AS pcs GROUP BY pcs.ID'); $subcategoriesStmt->execute(); $subcategories = $subcategoriesStmt->fetchAll(PDO::FETCH_ASSOC); // 关键一步:转成数组 foreach ($categories as $category) { // 简化选中状态的判断逻辑 $class = isset($_GET['categorie']) && $category["name"] === $_GET['categorie'] ? 'class="selected"' : ''; ?> <li><a href="?categorie=<?php echo $category["name"]; ?>" <?php echo $class; ?>><?php echo $category["name"] . ' ('. $category["amount"] .')'; ?></a></li> <?php // 遍历子分类数组,每个父分类都会从头开始遍历 foreach ($subcategories as $subcategory) { if($subcategory['categoryID'] == $category['ID']) { // 给子分类也加上选中状态判断(可选,看你需求) $subClass = isset($_GET['categorie']) && $subcategory["name"] === $_GET['categorie'] ? 'class="selected"' : ''; ?> <li class="sub-cat"><a href="?categorie=<?php echo $subcategory["name"]; ?>" <?php echo $subClass; ?>><?php echo $subcategory["name"]; ?></a></li> <?php } } }
方法二:每次遍历子分类前重置指针
如果你不想转数组,也可以在每次遍历子分类之前,调用rewind()方法把PDOStatement的指针重置到结果集开头:
// 原查询部分不变... foreach ($categories as $category) { // 选中状态判断... ?> <li><a href="?categorie=<?php echo $category["name"]; ?>" <?php echo $class; ?>><?php echo $category["name"] . ' ('. $category["amount"] .')'; ?></a></li> <?php // 重置子分类结果集的指针到开头 $subcategories->rewind(); foreach ($subcategories as $subcategory) { if($subcategory['categoryID'] == $category['ID']) { // 子分类输出... } } }
这种方法也能解决问题,但相比转数组,它依赖PDOStatement的内部状态,稳定性稍差一点,所以更推荐第一种方法。
方法三:优化数据库查询(性能更优)
如果想进一步优化,可以通过一次查询把父分类和子分类的数据都关联起来,减少数据库交互次数。比如用LEFT JOIN查询,然后在PHP里把数据整理成父分类对应子分类的结构:
// 一次查询拿到所有父分类和子分类数据 $combinedQuery = $conn->query(' SELECT pc.name AS parent_name, pc.ID AS parent_id, COUNT(p.id) AS parent_amount, pcs.name AS sub_name, pcs.ID AS sub_id, pcs.categoryID AS sub_parent_id FROM productcategory AS pc LEFT JOIN productsubcategory AS pcs ON pc.ID = pcs.categoryID LEFT JOIN products AS p ON p.categoryID = pc.ID WHERE p.available = 1 GROUP BY pc.ID, pcs.ID '); $combinedQuery->execute(); $combinedData = $combinedQuery->fetchAll(PDO::FETCH_ASSOC); // 整理数据:父分类ID为键,值包含父分类信息和子分类列表 $categoryTree = []; foreach ($combinedData as $item) { $parentId = $item['parent_id']; if (!isset($categoryTree[$parentId])) { $categoryTree[$parentId] = [ 'name' => $item['parent_name'], 'amount' => $item['parent_amount'], 'subcategories' => [] ]; } if (!empty($item['sub_name'])) { $categoryTree[$parentId]['subcategories'][] = [ 'name' => $item['sub_name'], 'id' => $item['sub_id'] ]; } } // 遍历整理好的分类树输出 foreach ($categoryTree as $category) { $class = isset($_GET['categorie']) && $category["name"] === $_GET['categorie'] ? 'class="selected"' : ''; ?> <li><a href="?categorie=<?php echo $category["name"]; ?>" <?php echo $class; ?>><?php echo $category["name"] . ' ('. $category["amount"] .')'; ?></a></li> <?php foreach ($category['subcategories'] as $subcategory) { $subClass = isset($_GET['categorie']) && $subcategory["name"] === $_GET['categorie'] ? 'class="selected"' : ''; ?> <li class="sub-cat"><a href="?categorie=<?php echo $subcategory["name"]; ?>" <?php echo $subClass; ?>><?php echo $subcategory["name"]; ?></a></li> <?php } }
这种方法只需要一次数据库查询,性能更好,而且代码结构更清晰,适合数据量较大的场景。
内容的提问来源于stack exchange,提问作者Pieter
相关产品推荐
相关产品推荐

