Древовидные комментарии.

Написана 7 Февраля, 2012 в 19:47. Автор: borN_free   |   Теги: комментарии, tree, comments Комментарии 7

Древовидные комментарииТема древовидных комментариев заезжена до ужаса, но, тем не менее, хотелось бы о ней поговорить и здесь. Я не буду затрагивать сторону Nested Sets, а покажу простую реализацию с помощью foreign key и рекурсии.

Покажу на примере того, как реализованы комментарии в моем блоге (упрощенный вариант). Для начала давайте создадим таблицу:

CREATE TABLE 'comment' (
 'id' int(11) NOT NULL AUTO_INCREMENT,
 'parent_id' int(11) DEFAULT NULL,
 'content' text COLLATE utf8_unicode_ci NOT NULL,  
 'author' varchar(128) COLLATE utf8_unicode_ci NOT NULL,
 PRIMARY KEY ('id'),
 KEY 'FK_parent_id` ('parent_id'),
 CONSTRAINT 'FK_parent_post' FOREIGN KEY ('parent_id') 
REFERENCES 'comment' ('id') ON DELETE CASCADE 
ON UPDATE CASCADE
) ENGINE=InnoDB;

Для простоты я убрал все лишние поля. Итак, самое главное - это хранить у каждого комментария его родителя. Условимся, что комментарии самового верхнего уровня имеют в качестве родителя NULL.

"Я хочу, чтобы при удалении комментария удалялись его дочерние" - скажете вы. Без проблем. Магия заключается в том, что мы добавляем внешний ключ (parent_id), который будет ссылаться на поле id. Теперь, если мы удалим комметарий, то каскадно удалятся все зависимые, без всяких написаний хранимых процедур или рекурсивного удаления через PHP. Все будет происходить на уровне СУРБД (MySQL например).

А сейчас нам надо вывести их на страницу. Здесь в любом случае понадобиться рекурсия. Приступим. Я покажу как выводятся комметарии здесь, на сайте. Смысл заключается в том, что выбираются все записи, рекурсивно складываются в не иерархический массив (этим мы избегаем использования рекурсивной функции при генерации HTML), и выводятся с отступами согласно "вложенности". Можете посмотреть исходный код страницы - там нет никаких вложенных div или ul-li, если конечно комментарии существуют к данному посту =). Приведу сразу ViewHelper с пояснениями:

class PostViewHelper {
    const NO_PARENT_ID = 0;
    private static $level = -1;

    public static function getCommentsTree($comments) {
        $cats = array();

        // проходим по ВСЕМ комментариям и формируем массив
        foreach ($comments as $comment) {
            // ключ массива - родитель текущего комментария
            $cats[(int) $comment->parent_id][] = array(
                'comment_id' => $comment->id,
                'content' => $comment->content,                
                'authorLink' => $comment->authorLink,
            );
        }
        // теперь просто вызываем рекурсивную функцию
        return self::buildTreelikeArray($cats, self::NO_PARENT_ID);
    }

    public static function buildTreelikeArray($cats, $parent_id) {
        // каждый раз увеличиваем статическую переменную уровня
        self::$level++;
        $result = array();

        if (is_array($cats) && isset($cats[$parent_id])) {
            foreach ($cats[$parent_id] as $cat) {
                $cat['level'] = self::$level;
                $result[] = $cat;
                $result = array_merge($result, self::buildTreelikeArray($cats, $cat['comment_id']));
                self::$level--;
                // для создания иерархического массива
                //$result[] = self::buildTreelikeArray($cats, $cat['comment_id']);
            }
        } else {
            return array();
        }
        return $result;
    }
}

Вот таким нехитрым способом можно работать с древовидными структурами. Это не всегда то, что нужно, но в большинстве случаем хватает. Кстати, никогда не доставайте в цикле по родителю его дочерние записи, потому как со временем ваша БД обязательно разрастется.

7 comments

+3 ответить
April 9, 2013 at 07:09 am

проверка работы формы

+4 ответить
June 6, 2015 at 08:24 pm

Answer test

-2 ответить
fegfejk:
September 27, 2015 at 04:53 pm

htjuhtujt76u

-2 ответить
April 8, 2016 at 04:10 pm

c

+3 ответить
April 30, 2016 at 03:21 pm

джьвма

-2 ответить
gdfg:
May 21, 2016 at 10:53 pm

gdfgdf

+1 ответить
April 15, 2016 at 05:21 pm

хорошая идея. может подскажите как этим классом пользоваться. я передаю выборку из БД в getCommentsTree() выходит не то и замичание что Trying to get property of non-object в строках 'comment_id' => $comment->id и тд

Оставьте свой комментарий:

Поля с * обязательны.