来自 MySQL 查询的多个嵌套数组 PHP

Multiple nested array from MySQL query in PHP

我正在使用 foreach 循环访问嵌套数组中的记录。

我需要嵌套3个数组(所以第一个数组包含一个数组,它也包含一个数组)。我在 2 个阵列上取得了成功,但我无法让 3 个阵列正常工作。

我的代码使用了 2 个数组(效果很好),但我无法嵌套 3 个数组。

这是我想要的结果:

[
  {
    "site_id": "1",
    "user_plants": [
      {
        "user_plant_id": "1",
        "site_id": "1",
        "plant_id": "1",
        "plant_images": [
          {
            "plant_image_id": "1"
          },
          {
            "plant_image_id": "2"
          },
          {
            "plant_image_id": "3"
          },
        ]
      }
    ]
  }
]

我当前的代码:

 $query = "SELECT A.site_id FROM sites A WHERE A.user_id='".$user_id."' GROUP BY A.site_id";
    $result = $this->conn->query($query);

    $json_response = array();
    $sites = array();
    if ($result-> num_rows > 0) {

        while ($item = $result->fetch_object())
            $sites[] = $item;

        foreach($sites as $item) {
            $row_array = (array)$item;
            $site_id = $item->site_id;

            $user_plants = "SELECT A.user_plant_id, A.site_id, A.plant_id FROM user_plants A RIGHT JOIN sites B ON A.site_id ='".$site_id."' 
            JOIN plants C ON A.plant_id = C.plant_id GROUP BY A.user_plant_id";
            $resultSet = $this->conn->query($user_plants);

            $user_plants = array();
            if ($resultSet-> num_rows > 0) {

                while ($item = $resultSet->fetch_object())
                    $user_plants[] = $item;

                foreach ($user_plants as $item) {
                    $row_array['user_plants'][] = (array)$item;

                    $plant_id = $item->plant_id;
                    $user_plant_id = $item->user_plant_id;


                    $plant_images = "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";
                    $resultSet = $this->conn->query($plant_images);

                    $plant_images = array();
                    if ($resultSet->num_rows > 0) {

                        while ($item = $resultSet->fetch_object())
                            $plant_images[] = $item;


                        foreach ($plant_images as $item) {

                            $row_array['user_plants'][]['plant_images'][] = $item;
                        }

                    } else if ($resultSet->num_rows == 0) {
                        $row_array['plant_images'] = [];
                    }
                }

                $json_response[] = $row_array;
            }
            
        }
        
    }

    return $json_response;

以上代码的结果:

[
  {
    "site_id": "1",
    "user_plants": [
      {
        "user_plant_id": "1",
        "site_id": "1",
        "plant_id": "1"
      },
      {
      "plant_images": [
          {
            "plant_image_id": "1"
          },
          {
            "plant_image_id": "2"
          },
          {
            "plant_image_id": "3"
          },
        ]
      }
    ]
  }
]

我应该如何调整上面的 foreach 循环以适应这种情况?

这段代码还有很多改进的余地,但我忽略了这一点,并尽量使代码与本示例中的代码保持一致。

主要变化是:

  • 创建一个临时变量$user_plant_array,我们将“plant_images”存储在
  • 在循环结束时将该临时变量推送到 $site_array
  • 重命名一些循环变量,以便更容易识别您引用的内容
$json_response = array();
$sites = array();
if ($result->num_rows > 0) {

    while ($site = $result->fetch_object()) {
        $sites[] = $site;
    }

    foreach ($sites as $site) {
        $site_array = (array)$site;
        $site_id = $site->site_id;

        $user_plants = "SELECT A.user_plant_id, A.site_id, A.plant_id FROM user_plants A RIGHT JOIN sites B ON A.site_id ='" . $site_id . "' 
            JOIN plants C ON A.plant_id = C.plant_id GROUP BY A.user_plant_id";
        $resultSet = $this->conn->query($user_plants);

        $user_plants = array();
        if ($resultSet->num_rows > 0) {

            while ($user_plant = $resultSet->fetch_object())
                $user_plants[] = $user_plant;

            foreach ($user_plants as $user_plant) {
                // create a temporary variable here that we will map
                // all "plant_images" to
                $user_plant_array = (array)$user_plant;

                $plant_id = $user_plant->plant_id;
                $user_plant_id = $user_plant->user_plant_id;


                $plant_images = "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";
                $resultSet = $this->conn->query($plant_images);

                $plant_images = array();
                if ($resultSet->num_rows > 0) {

                    while ($plant_image = $resultSet->fetch_object())
                        $plant_images[] = $plant_image;


                    foreach ($plant_images as $plant_image) {
                        $user_plant_array['plant_images'][] = $plant_image;
                    }
                    
                } else if ($resultSet->num_rows == 0) {
                    $user_plant_array['plant_images'] = [];
                }
                
                // the temporary variable now contains all "plant_images"
                // now we can push that to the site array
                $site_array['user_plants'][] = $user_plant_array;
            }

            $json_response[] = $site_array;
        }
    }
}

return $json_response;

通过一些代码改进创建一个单独的答案作为替代解决方案。

“改进”可读性更高 and/or 性能更高。

我建议作为“改进”的一些主要更改已在此示例中实现。主要是:

  • 使用准备好的 SQL 语句(并不总是必需的,但使用起来是个好习惯,尤其是在任何接受用户输入的地方,也可以使代码更清晰)
  • 减少循环次数(在一些地方你循环只是为了创建一个数组然后再次循环)
  • Returning/continuing 尽可能早(有助于防止不必要的嵌套)
  • 删除不必要的 if 语句(例如,如果结果为空,将跳过大部分 while 循环 - 事先检查并非完全必要)
  • 更具可读性的变量名称(新编码人员通常会尝试缩写很多变量并且经常使用过头 - 使它们可读会在调试时为您节省大量时间)

使用 mysqli 的代码可能不是最好的,因为我通常使用 PDO

function getSitesData() {
    // assumes that $user_id is set somewhere before this
    // assumes that $this->conn references a valid database connection

    $sql = "SELECT A.site_id FROM sites A WHERE A.user_id = ? GROUP BY A.site_id";

    $query = $this->conn->prepare($sql);
    $query->bind_param("i", $user_id);
    $query->execute();

    $site_result = $query->get_result();

    $sites = [];
    while ($site = $site_result->fetch_assoc()) {
        // using fetch_assoc gives us an associative array

        // initialise empty array
        $site["user_plants"] = [];

        // get user_plants
        $sql = "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";

        $query = $this->conn->prepare($sql);
        $query->bind_param("i", $site["site_id"]);
        $query->execute();

        $user_plant_result = $query->get_result();
        while ($user_plant = $user_plant_result->fetch_assoc()) {
            // intialise empty array
            $user_plant["plant_images"] = [];

            // get plant images
            $sql = "SELECT A.plant_image_id FROM plants_images A WHERE A.plant_id = ? UNION SELECT B.plant_image_id FROM user_plant_image B JOIN user_plants C ON B.user_plant_id = ? WHERE C.user_id = ? GROUP BY B.plant_image_id ORDER BY plant_image_id";

            $query = $this->conn->prepare($sql);
            $query->bind_param("iii", $user_plant["plant_id"], $user_plant["user_plant_id"], $user_id);
            $query->execute();

            $plant_image_result = $query->get_result();
            while ($plant_image = $plant_image_result->fetch_assoc()) {
                $user_plant["plant_images"][] = $plant_image;
            }

            $sites["user_plants"][] = $user_plant;
        }

        $sites[] = $site;
    }

    return $sites;
}