PHP中MySQL查询中的多个嵌套数组

Multiple nested array from MySQL query in PHP

提问人:mohammad alvari 提问时间:4/21/2022 更新时间:4/22/2022 访问量:856

问:

我正在使用 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 循环来满足这种情况?

php mysql 维数 组嵌套

评论

0赞 mickmackusa 4/21/2022
这些嵌套/迭代查询从一开始就是一个坏主意。在修复结果数组之前,我们需要您通过一次访问数据库来收集所有数据。请创建一个最小但足够的 db 小提琴,以便我们为您提供适当的帮助。
0赞 Rylee 4/21/2022
嵌套循环的提示;在尝试调试时,重用变量名称可能会使其混淆。这里的主要问题是,您没有在添加时引用索引,因此它而是推送到该数组而不是添加属性。["user_plants"]["plant_images"]
0赞 mickmackusa 5/1/2022
@moh下面的答案都不是我所说的“专业”。使用父查询的结果集的迭代查询从来都不是一件好事。使用嵌套的 foreach 循环来做这件事是超级不是很好。当只应进行一次访问时,对数据库的潜在访问太多。
0赞 mohammad alvari 5/9/2022
亲爱的@mickmackusa,你能告诉我我是如何在一次旅行中专业地做到这一点吗?谢谢!
0赞 mickmackusa 5/10/2022
如果我有时间为你做这项工作,我会首先要求你在 phpize.online 演示链接中提供基本的数据库数据。一旦你提供了它,它应该是任何下降的php dev来构建必要的sql和php。

答:

0赞 Rylee 4/21/2022 #1

此代码还有很大的改进空间,但我忽略了这一点,并试图在此示例中保持代码与您的代码匹配。

主要变化是:

  • 创建一个临时变量,我们存储“plant_images”$user_plant_array
  • 将该临时变量推送到循环末尾$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;
0赞 Rylee 4/22/2022 #2

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

“改进”是更高的可读性和/或更高的性能。

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

  • 使用准备好的 SQL 语句(并不总是必需的,但使用良好的做法,尤其是在接受用户输入的任何内容中,也可以使代码更简洁)
  • 减少循环数量(在一些地方,您只是为了创建一个数组而循环,然后再次循环)
  • 尽可能尽早返回/继续(有助于防止不必要的嵌套)
  • 删除不必要的 if 语句(例如,如果结果为空,将跳过大多数 while 循环 - 事先检查并非完全必要)
  • 更易读的变量名称(对于新编码人员来说,尝试缩写很多变量是很常见的,并且经常走得太远 - 使它们可读将在调试时节省大量时间)

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

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;
}