
本文详解如何通过优化 foreach 循环结构与变量作用域,将 mysql 查询结果准确组织为三层嵌套 json 结构(sites → user_plants → plant_images),避免层级错位,并提供安全、可维护的实现方案。
本文详解如何通过优化 foreach 循环结构与变量作用域,将 mysql 查询结果准确组织为三层嵌套 json 结构(sites → user_plants → plant_images),避免层级错位,并提供安全、可维护的实现方案。
在 PHP 中从 MySQL 构建多层嵌套数组时,常见错误是复用循环变量名(如多次使用 $item)和错误地向父数组追加子项(例如直接 []['plant_images'][]),导致数据脱离预期层级。你遇到的问题正是第二层(user_plants)与第三层(plant_images)未正确归属:原代码中 $row_array['user_plants'][]['plant_images'][] 实际创建了一个全新的空数组元素再附加图片,而非将图片挂载到已存在的用户植物对象下。
✅ 正确做法:逐层构造,用临时变量承载子结构
核心原则是:每层嵌套都应先完整构建该层级的数据结构,再整体追加到上一层。以三层结构为例:
- 外层:遍历 sites,初始化 $site_array;
- 中层:遍历该 site 下的 user_plants,为每个 user_plant 创建独立的 $user_plant_array(含基础字段);
- 内层:查询并填充 $user_plant_array['plant_images'],最后将完整的 $user_plant_array 推入 $site_array['user_plants']。
以下是修正后的关键逻辑(已去除 SQL 注入风险提示,实际生产环境务必使用预处理语句):
$json_response = [];
if ($result->num_rows > 0) {
while ($site = $result->fetch_object()) {
$sites[] = $site;
}
foreach ($sites as $site) {
// ① 初始化站点级数组
$site_array = (array)$site;
$site_array['user_plants'] = []; // 显式初始化,避免 undefined index
$site_id = $site->site_id;
// ② 查询该站点下的用户植物
$user_plants_query = "SELECT A.user_plant_id, A.site_id, A.plant_id
FROM user_plants A
RIGHT JOIN sites B ON A.site_id = ?
JOIN plants C ON A.plant_id = C.plant_id
GROUP BY A.user_plant_id";
// ⚠️ 强烈建议改用 PDO/MySQLi 预处理防止 SQL 注入(此处保留原风格便于理解)
$resultSet = $this->conn->query(str_replace('?', "'$site_id'", $user_plants_query));
if ($resultSet && $resultSet->num_rows > 0) {
$user_plants = [];
while ($up = $resultSet->fetch_object()) {
$user_plants[] = $up;
}
foreach ($user_plants as $up) {
// ③ 为每个用户植物创建独立容器
$user_plant_array = (array)$up;
$user_plant_array['plant_images'] = []; // 初始化图片数组
$plant_id = $up->plant_id;
$user_plant_id = $up->user_plant_id;
// ④ 查询该用户植物关联的图片(合并 plants_images + user_plant_image)
$images_query = "SELECT A.plant_image_id FROM plants_images A WHERE A.plant_id = '$plant_id'
UNION
SELECT B.plant_image_id FROM user_plant_image B
JOIN user_plants C ON B.user_plant_id = '$user_plant_id'
WHERE C.user_id = '$user_id'
GROUP BY B.plant_image_id ORDER BY plant_image_id";
$imgResult = $this->conn->query($images_query);
if ($imgResult && $imgResult->num_rows > 0) {
while ($img = $imgResult->fetch_object()) {
$user_plant_array['plant_images'][] = (array)$img; // 转为关联数组更易 JSON 化
}
}
// ✅ 关键:将完整构建的 $user_plant_array 追加到站点
$site_array['user_plants'][] = $user_plant_array;
}
}
$json_response[] = $site_array;
}
}
return $json_response;? 重要注意事项
-
SQL 注入风险:原文本拼接 SQL 极不安全。必须改用 MySQLi 预处理 或 PDO 的 prepare()/execute(),例如:
$stmt = $this->conn->prepare("SELECT ... WHERE A.site_id = ?"); $stmt->bind_param("i", $site_id); $stmt->execute(); - 空结果处理:$site_array['user_plants'] 和 $user_plant_array['plant_images'] 均显式初始化为空数组,确保 JSON 输出结构稳定(避免 null 或缺失字段)。
- 性能优化建议:当前为 N+1 查询模式(1 次查 site,N 次查 user_plants,N×M 次查 plant_images)。生产环境推荐改为单次 JOIN 查询 + PHP 端分组聚合,大幅提升效率。
- 类型一致性:fetch_object() 返回对象,转为 (array) 后再赋值,确保最终 json_encode() 输出为标准关联数组(而非对象),符合示例 JSON 格式。
通过清晰分离层级、避免变量覆盖、显式初始化子数组,即可精准生成目标三层嵌套结构。记住:“先造车,再装车”,而非“边造边扔零件”——这是处理多层嵌套数据的核心思维。
立即学习“PHP免费学习笔记(深入)”;



















