【问题标题】:PHP how to loop through multiple comment levelsPHP如何循环多个评论级别
【发布时间】:2014-06-26 10:29:34
【问题描述】:

我的数据库结构目前看起来像这样(用于评论系统):

但是,我完全不知道如何遍历这些数据,尤其是嵌套的 cmets/不同级别。

目前,我循环遍历数据并显示所有level 0 cmets,对于每个0级评论,循环并找到它对应的level 1评论。但是,最多可以有 10 个关卡,而像我这样循环遍历所有 10 个关卡,我觉得效率非常低。

replyTo 列指的是它所回复的评论的 ID。如果数字为 0,则表示该评论不是回复。例如,对于上面的表格,我想用 PHP 循环显示一些东西(从最新到最旧):

hello
    what do you want
    test
        Good!
test
This is a page!
    test

【问题讨论】:

  • 您需要一个递归函数来从数据库中读取您的 cmets 并显示它们
  • 递归可能是你的朋友。但是,这将需要大量查询并且可能会减慢速度。另一种方法是更改​​为嵌套集模型,但是当添加/删除大量数据时,这会很困难(这在线程评论系统上可能会发生很多次)。
  • @Kickstart 大量查询或用一个查询读取所有 cmets 并在该数组上循环很多。但可能会更快,然后再次为每个评论触发查询以查找子 cmets。
  • @TiMESPLiNTER - 可以进行 1 次查询,但随后您不得不手动循环遍历数组以查找子记录(并且由于多个记录可能是您不能仅使用的父项的子项) parent id 作为数组的索引)。这是数据库更擅长的事情。
  • @TiMESPLiNTER - 它会随着数据量的变化而变化。这两种解决方案都不适用于大量数据。 MySQL 具有索引的好处,而 php 的好处是不需要徘徊到可能不同的服务器。 PHP 还有一个缺点,就是可能需要更多内存来存储大量帖子。

标签: php mysql loops nested


【解决方案1】:

你必须选择:

大量查询

您只需在循环中选择所需的 cmets:

<?php

function displayCommentsRecursive($stmnt, $replyTo = 0) {
    // Select the top comments first
    $stmnt->execute(array(0));
    $result = $stmnt->fetchAll();

    foreach($result as $res) {
        // display comment
        echo $res->user , ': ' , $res->text;

        echo displayCommentsRecursive($res->ID);
    }
}

$stmntGetComments = $pdo->prepare("
    SELECT ID, user, text
    FROM comment
    WHERE replyTo = ?
");

displayCommentsRecursive($stmntGetComments);

大量迭代

您只需触发一次查询,然后从“评论池”中挑选出您需要的 cmets:

<?php

function displayCommentsRecursive(array $comments, $replyTo = 0) {
    foreach($result as $res) {
        if($replyTo != $res->replyTo)
            continue;

        // display comment
        echo $res->user , ': ' , $res->text;

        echo displayCommentsRecursive($comments, $res->ID);
    }
}

$stmntGetComments = $pdo->prepare("
    SELECT ID, user, text, replyTo
    FROM comment
");

// Select all comments at once
$stmntGetComments->execute(array(0));
$comments = $stmntGetComments->fetchAll();

displayCommentsRecursive($comments);

【讨论】:

    【解决方案2】:

    示例 php 脚本(假设将数据库连接类传递给函数)。

    <?php
    
    post_replies($db, 0, 0);
    
    function post_replies($db, $cnt=0, $parent_id=0)
    {
        $messages = array();
        $sql = "SELECT id, message
                FROM some_table
                WHERE replyTo = ".(int)$parent_id."
                ORDER BY date";
        $results = $db->query($sql);
        while($row = $db->fetch_assoc())
        {
            $messages[] = $row;
        }
        foreach($messages AS $message)
        {
            echo str_repeat("\t", $cnt).$message['message'];
            post_replies($db, $cnt + 1, $message['id'])
        }
    }
    
    ?>
    

    可以稍微调整一下,以避免对没有回复的消息进行进一步查询:-

    <?php
    
    post_replies($db, 0, 0);
    
    function post_replies($db, $cnt=0, $parent_id=0)
    {
        $messages = array();
        $sql = "SELECT a.id, a.message, COUNT(b.id) AS child_count
                FROM some_table a
                LEFT OUTER JOIN some_table b
                ON a.id = b.parent_id
                WHERE a.replyTo = ".(int)$parent_id."
                GROUP BY a.id, a.message
                ORDER BY a.date";
        $results = $db->query($sql);
        while($row = $db->fetch_assoc())
        {
            $messages[] = $row;
        }
        foreach($messages AS $message)
        {
            echo str_repeat("\t", $cnt).$message['message'];
            if ($message['child_count'] > 0)
            {
                post_replies($db, $cnt + 1, $message['id'])
            }
        }
    }
    
    ?>
    

    编辑

    想了想,决定玩一玩。由于您有最大数量的级别,这可能通过详细的 SQL 来实现。

    这可以通过获取所有回复为 0 的消息,然后将其与所有回复为 0 和子记录的消息合并。并对大子记录等做同样的事情。

    在排序上稍加修饰,这会使事情恢复到正确的顺序:-

    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            b1.date AS b1Date, b1.id AS b1Id, 
            b2.date AS b2Date, b2.id AS b2Id, 
            b3.date AS b3Date, b3.id AS b3Id, 
            b4.date AS b4Date, b4.id AS b4Id, 
            b5.date AS b5Date, b5.id AS b5Id, 
            b6.date AS b6Date, b6.id AS b6Id, 
            b7.date AS b7Date, b7.id AS b7Id, 
            b8.date AS b8Date, b8.id AS b8Id, 
            CONCAT(REPEAT('-', 9), b8.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    INNER JOIN some_table b1 ON b0.id = b1.replyTo
    INNER JOIN some_table b2 ON b1.id = b2.replyTo
    INNER JOIN some_table b3 ON b2.id = b3.replyTo
    INNER JOIN some_table b4 ON b3.id = b4.replyTo
    INNER JOIN some_table b5 ON b4.id = b5.replyTo
    INNER JOIN some_table b6 ON b5.id = b6.replyTo
    INNER JOIN some_table b7 ON b6.id = b7.replyTo
    INNER JOIN some_table b8 ON b7.id = b8.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            b1.date AS b1Date, b1.id AS b1Id, 
            b2.date AS b2Date, b2.id AS b2Id, 
            b3.date AS b3Date, b3.id AS b3Id, 
            b4.date AS b4Date, b4.id AS b4Id, 
            b5.date AS b5Date, b5.id AS b5Id, 
            b6.date AS b6Date, b6.id AS b6Id, 
            b7.date AS b7Date, b7.id AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            CONCAT(REPEAT('-', 8), b7.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    INNER JOIN some_table b1 ON b0.id = b1.replyTo
    INNER JOIN some_table b2 ON b1.id = b2.replyTo
    INNER JOIN some_table b3 ON b2.id = b3.replyTo
    INNER JOIN some_table b4 ON b3.id = b4.replyTo
    INNER JOIN some_table b5 ON b4.id = b5.replyTo
    INNER JOIN some_table b6 ON b5.id = b6.replyTo
    INNER JOIN some_table b7 ON b6.id = b7.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            b1.date AS b1Date, b1.id AS b1Id, 
            b2.date AS b2Date, b2.id AS b2Id, 
            b3.date AS b3Date, b3.id AS b3Id, 
            b4.date AS b4Date, b4.id AS b4Id, 
            b5.date AS b5Date, b5.id AS b5Id, 
            b6.date AS b6Date, b6.id AS b6Id, 
            NULL AS b7Date, NULL AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            CONCAT(REPEAT('-', 7), b6.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    INNER JOIN some_table b1 ON b0.id = b1.replyTo
    INNER JOIN some_table b2 ON b1.id = b2.replyTo
    INNER JOIN some_table b3 ON b2.id = b3.replyTo
    INNER JOIN some_table b4 ON b3.id = b4.replyTo
    INNER JOIN some_table b5 ON b4.id = b5.replyTo
    INNER JOIN some_table b6 ON b5.id = b6.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            b1.date AS b1Date, b1.id AS b1Id, 
            b2.date AS b2Date, b2.id AS b2Id, 
            b3.date AS b3Date, b3.id AS b3Id, 
            b4.date AS b4Date, b4.id AS b4Id, 
            b5.date AS b5Date, b5.id AS b5Id, 
            NULL AS b6Date, NULL AS b6Id, 
            NULL AS b7Date, NULL AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            CONCAT(REPEAT('-', 6), b5.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    INNER JOIN some_table b1 ON b0.id = b1.replyTo
    INNER JOIN some_table b2 ON b1.id = b2.replyTo
    INNER JOIN some_table b3 ON b2.id = b3.replyTo
    INNER JOIN some_table b4 ON b3.id = b4.replyTo
    INNER JOIN some_table b5 ON b4.id = b5.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            b1.date AS b1Date, b1.id AS b1Id, 
            b2.date AS b2Date, b2.id AS b2Id, 
            b3.date AS b3Date, b3.id AS b3Id, 
            b4.date AS b4Date, b4.id AS b4Id, 
            NULL AS b5Date, NULL AS b5Id, 
            NULL AS b6Date, NULL AS b6Id, 
            NULL AS b7Date, NULL AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            CONCAT(REPEAT('-', 5), b4.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    INNER JOIN some_table b1 ON b0.id = b1.replyTo
    INNER JOIN some_table b2 ON b1.id = b2.replyTo
    INNER JOIN some_table b3 ON b2.id = b3.replyTo
    INNER JOIN some_table b4 ON b3.id = b4.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            b1.date AS b1Date, b1.id AS b1Id, 
            b2.date AS b2Date, b2.id AS b2Id, 
            b3.date AS b3Date, b3.id AS b3Id, 
            NULL AS b4Date, NULL AS b4Id, 
            NULL AS b5Date, NULL AS b5Id, 
            NULL AS b6Date, NULL AS b6Id, 
            NULL AS b7Date, NULL AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            CONCAT(REPEAT('-', 4), b3.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    INNER JOIN some_table b1 ON b0.id = b1.replyTo
    INNER JOIN some_table b2 ON b1.id = b2.replyTo
    INNER JOIN some_table b3 ON b2.id = b3.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            b1.date AS b1Date, b1.id AS b1Id, 
            b2.date AS b2Date, b2.id AS b2Id, 
            NULL AS b3Date, NULL AS b3Id, 
            NULL AS b4Date, NULL AS b4Id, 
            NULL AS b5Date, NULL AS b5Id, 
            NULL AS b6Date, NULL AS b6Id, 
            NULL AS b7Date, NULL AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            CONCAT(REPEAT('-', 3), b2.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    INNER JOIN some_table b1 ON b0.id = b1.replyTo
    INNER JOIN some_table b2 ON b1.id = b2.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            b1.date AS b1Date, b1.id AS b1Id, 
            NULL AS b2Date, NULL AS b2Id, 
            NULL AS b3Date, NULL AS b3Id, 
            NULL AS b4Date, NULL AS b4Id, 
            NULL AS b5Date, NULL AS b5Id, 
            NULL AS b6Date, NULL AS b6Id, 
            NULL AS b7Date, NULL AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            CONCAT(REPEAT('-', 2), b1.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    INNER JOIN some_table b1 ON b0.id = b1.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            b0.date AS b0Date, b0.id AS b0Id, 
            NULL AS b1Date, NULL AS b1Id, 
            NULL AS b2Date, NULL AS b2Id, 
            NULL AS b3Date, NULL AS b3Id, 
            NULL AS b4Date, NULL AS b4Id, 
            NULL AS b5Date, NULL AS b5Id, 
            NULL AS b6Date, NULL AS b6Id, 
            NULL AS b7Date, NULL AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            CONCAT(REPEAT('-', 1), b0.message)
    FROM some_table a
    INNER JOIN some_table b0 ON a.id = b0.replyTo
    WHERE a.replyTo = 0
    UNION ALL
    SELECT a.date AS aDate, a.id AS aId, 
            NULL AS b0Date, NULL AS b0Id, 
            NULL AS b1Date, NULL AS b1Id, 
            NULL AS b2Date, NULL AS b2Id, 
            NULL AS b3Date, NULL AS b3Id, 
            NULL AS b4Date, NULL AS b4Id, 
            NULL AS b5Date, NULL AS b5Id, 
            NULL AS b6Date, NULL AS b6Id, 
            NULL AS b7Date, NULL AS b7Id, 
            NULL AS b8Date, NULL AS b8Id, 
            a.message
    FROM some_table a
    WHERE a.replyTo = 0
    ORDER BY aDate DESC, aID, 
            IFNULL(b0Date, '2099-12-31') DESC, b0Id, 
            IFNULL(b1Date, '2099-12-31') DESC, b1Id, 
            IFNULL(b2Date, '2099-12-31') DESC, b2Id, 
            IFNULL(b3Date, '2099-12-31') DESC, b3Id, 
            IFNULL(b4Date, '2099-12-31') DESC, b4Id, 
            IFNULL(b5Date, '2099-12-31') DESC, b5Id, 
            IFNULL(b6Date, '2099-12-31') DESC, b6Id, 
            IFNULL(b7Date, '2099-12-31') DESC, b7Id, 
            IFNULL(b8Date, '2099-12-31') DESC, b8Id
    

    它的 SQL 小提琴:-

    http://www.sqlfiddle.com/#!2/775405/12

    【讨论】:

    • 进行了更改以演示在单个 SQL 中执行此操作的一种可能(但非常冗长)的方式,而无需重复循环遍历返回的所有记录的数组。
    猜你喜欢
    • 1970-01-01
    • 2021-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-09
    相关资源
    最近更新 更多