在嵌套 While 循环中输出数据透视表/链接表的内容 - MySQL / PHP PDO

pau*_*o77 5 php mysql sql nested

我遇到了一种情况,我试图从与 PHP 的多对多关系输出 MySQL 数据透视表/链接表的内容。

在下面的代码中,我有一系列包含图像的板。实际的板预览是使用 PHP 的第一个块输出的,但在其中我需要一个嵌套的 while 循环来输出图像本身。

枢轴/链接表被称为boards_images有两列board_id,并且这些都是表和表image_id的外键。下面的主要代码下面给出了表格的表示。boardsimages

因为某些板的图像已经存在于其他板上,所以我显然需要某种类型的条件逻辑,以便在相关板存在时输出图像。

每个板预览只会显示四个图像,因此我需要LIMIT 4向 MySQL 添加一个子句

我的问题

解决这个问题的最佳方法是什么,我:

a) 需要执行两次数据库调用,一次在父 while 循环中,一次在嵌套 while 循环中,或者我是否需要使用多个 JOIN 从父 while 循环中的 MySQL 数据库获取所有信息?

b) 如何实际输出数据透视表/链接表的内容?我似乎不知道如何做到这一点。

<?php 

    // $db_id is a variable created from a user login $_SESSION value

    $sql = "SELECT boards.board_id, boards.board_name, users.user_id 
    FROM boards
    JOIN users ON boards.user_id = users.user_id 
    WHERE users.user_id = :user_id
    ORDER BY boards.board_id DESC";

    $stmt = $connection->prepare($sql);
    $stmt -> execute([
        ':user_id' => $db_id
    ]);
    
    // parent while loop that outputs the board name

    while ($row = $stmt->fetch()) {
        $dbBoardname = htmlspecialchars($row['board_name']);
?>
    <div class="board-component">
        <h2><?= $dbBoardname; ?></a></h2>

        <?php

            // --- NESTED INNER WHILE LOOP

            $SQL2 = "SELECT boards_images.board_id, boards_images.image_id, images.filename
            FROM boards_images
            JOIN images ON boards_images.image_id = images.image_id
            WHERE boards_images.board_id = :board_id
            LIMIT 4";

            $stmt2 = $connection->prepare($SQL2);
            $stmt2 -> execute([
                ':board_id' => $dbBoardId
            ]);

            while ($row2 = $stmt2->fetch()) {          
                $dbImageId = htmlspecialchars($row['image_id']);
                $dbImageFilename = htmlspecialchars($row['filename']);
            ?>
                
             <img src='<?= $wwwRoot . "/images-lib/{$dbImageFilename}" ?>' >

        <?php } ?> <!-- end of nested while loop -->
    </div>
<?php } ?> <!-- end of parent while loop -->
Run Code Online (Sandbox Code Playgroud)

表格的表示

// 'fk' stands for foreign key

// BOARDS_IMAGES LINKING / PIVOT TABLE
+----------------+----------------+
|  board_id (fk) | image_id (fk)  |
+----------------+----------------+
|  1             |  23            |         
|  1             |  106           |
|  1             |  55            |
|  1             |  22            |
+----------------+----------------+

// BOARDS TABLE
+-------------+--------------+---------------+
|  board_id   |  board_name  |  user_id (fk) |
----------------------------------------------
|  1          |  London      |  21           |
|  2          |  France      |  21           |
+-------------+--------------+---------------+

// IMAGES TABLE
+-------------+--------------------+---------------+
|  image_id   |  filename          | user_id (fk)  |
---------------------------------------------------+
|  23         |  BigBen.jpeg       | 21            |
|  106        |  TowerBridge.jpeg  | 21            |
|  55         |  TheMall.jpg       | 21            |
|  22         |  BuckPalace.jpg    | 21            |
+-------------+--------------------+---------------+

// USERS TABLE
+-----------------+----------------+
|  user_id        |  username      |
+-----------------+----------------+
|   21            |  johndoe       |         
+-----------------+----------------+
Run Code Online (Sandbox Code Playgroud)

Sim*_*non 1

a) 需要执行两次数据库调用,一次在父 while 循环中,一次在嵌套 while 循环中,或者我是否需要使用多个 JOIN 从父 while 循环中的 MySQL 数据库获取所有信息?

您可以执行一个查询来获取所有必要的数据,我们可以限制 php 中显示的图像数量,并且不需要连接到 users 表,因为在boards 表中有 FK:

SELECT boards.board_name, images.filename
FROM boards
INNER JOIN boards_images on boards_images.board_id = boards.board_id
INNER JOIN images        on boards_images.image_id = images.image_id
WHERE boards.user_id = :user_id
Run Code Online (Sandbox Code Playgroud)

b) 如何实际输出数据透视表/链接表的内容?我似乎不知道如何做到这一点。

上述查询的输出结果如下:

board_name | filename
-------------------------------
London     |  BigBen.jpeg     
London     |  TowerBridge.jpeg
London     |  TheMall.jpg     
London     |  BuckPalace.jpg 
France     |  BigBen.jpeg     
France     |  TowerBridge.jpeg
France     |  TheMall.jpg     
France     |  BuckPalace.jpg 
Run Code Online (Sandbox Code Playgroud)

你的 php 循环可能看起来像这样:

<?php 

    // $db_id is a variable created from a user login $_SESSION value

    $sql = "SELECT boards.board_name, images.filename
            FROM boards
            INNER JOIN boards_images on boards_images.board_id = boards.board_id
            INNER JOIN images        on boards_images.image_id = images.image_id
            WHERE boards.user_id = :user_id";

    $stmt = $connection->prepare($sql);
    $stmt -> execute([
        ':user_id' => $db_id
    ]);

$dbBoardname_last = '';
$imgcount = 0;
 while ($row = $stmt->fetch()) {
   $dbBoardname = htmlspecialchars($row['board_name']);
   $dbImageFile = "$wwwRoot/images-lib/" . htmlspecialchars($row['filename']);
    
    //if the boardname is the same as the previous row only add images. 
   if($dbBoardname != $dbBoardname_last) 
   {
    //reset the image count for new boards
     $imgcount = 0;
     echo "<div class=\"board-component\"><h2>$dbBoardname</a></h2>";
   }
   
   //By counting the images within the loop we can avoid using multiple or nested queries to the DB 
   if($imgcount < 4) { 
    echo "<img src=\"$dbImageFile\">";
   }
   $imgcount++; 
   
   if($dbBoardname != $dbBoardname_last) 
   {
     echo '</div>';
   } 
    //record the last board_name to check if a new board element should be created
   $dbBoardname_last = $dbBoardname;
 }
?>
Run Code Online (Sandbox Code Playgroud)

注意:希望这段代码能按您的预期工作,但从长远来看,我认为最佳实践是将 SQL 输出解析为 JSON 对象并对其进行迭代,代码可能会更干净