PHP как перебирать несколько уровней комментариев

#php #mysql #loops #nested

#php #mysql #циклы #вложенный

Вопрос:

Моя структура базы данных в настоящее время выглядит следующим образом (для системы комментариев):

Однако я совершенно не понимаю, как перебирать такие данные, особенно с вложенными комментариями / разными уровнями.

В настоящее время я перебираю данные и отображаю все level 0 комментариев, а для каждого комментария 0 уровня просматриваю и нахожу соответствующий level 1 комментарий. Однако может быть не более 10 уровней, и я чувствую, что перебирать все 10 так, как я есть, было бы очень неэффективно.

replyTo Столбец ссылается на идентификатор комментария, на который он является ответом. Если число равно 0, это означает, что комментарий не является ответом. Например, для приведенной выше таблицы я бы хотел перебрать ее с помощью PHP, чтобы отобразить что-то как таковое (от самого последнего до самого старого):

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

Комментарии:

1. Вам нужна рекурсивная функция для чтения ваших комментариев из базы данных и их отображения

2. Рекурсия, вероятно, ваш друг. Однако это потребует большого количества запросов и может замедлиться. Альтернативой является переход на модель вложенного набора, но это затрудняется, когда добавляется / удаляется много данных (что в системе с потоковыми комментариями, вероятно, будет происходить много раз).

3. @Запустите много запросов или прочитайте все комментарии одним запросом и выполните много циклов по этому массиву. Но, вероятно, еще быстрее, чем снова запускать запрос для каждого комментария для поиска дочерних комментариев.

4. запрос @TiMESPLiNTER — 1 возможен, но тогда вам придется вручную перебирать массив, чтобы найти дочерние записи (а поскольку несколько записей могут быть дочерними для родительского элемента, вы не можете просто использовать родительский идентификатор в качестве индекса массива). Это то, в чем база данных намного лучше.

5. @TiMESPLiNTER — это зависит от объема данных. Ни одно из решений не подходит для больших объемов данных. Преимущество MySQL заключается в индексах, в то время как преимущество php заключается в том, что не нужно переходить на потенциально другой сервер. У Php также есть недостаток, заключающийся в том, что для большого количества сообщений может потребоваться гораздо больше памяти.

Ответ №1:

У вас есть варианты:

Много запросов

Вы просто выбираете нужные комментарии в цикле:

 <?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);
  

Много итераций

Вы запускаете запрос только один раз и выбираете нужные комментарии из «пула комментариев»:

 <?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.

Это работает путем получения всех сообщений с replyTo равным 0, а затем объединения их со всеми сообщениями с replyTo равным 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

Комментарии:

1. Внесено изменение, чтобы продемонстрировать возможный (но очень затянутый) способ сделать это в одном фрагменте SQL без необходимости многократного перебора массива всех возвращенных записей.