Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
PHP для продвинутых.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
12.54 Mб
Скачать

28. Многоуровневые комментарии. Алгоритм NestedSets. Модуль orm-mptt

Многоуровневые комментарии – это комментарии, у которых помимо уникального ключа id, есть id родителя (parent_id), который показывает вложенность. По умолчанию, parent_id равно 0. Что указывает на то, что данный комментарий относится к высшему уровню. Если комментируется не сама статья, а комментарий, то parent_id указывает id комментируемой записи. См. пример:

Статья

  1. Комментарий

3. Комментарий комментария 1.

4. Комментарий комментария 3.

5. Комментарий комментария 1.

  1. Комментарий

На первый взгляд, такую древовидную структуру комментариев создать не сложно. Однако MySQL не позволяет одним запросом вывести все комментарии. Придется делать цикл на вывод основных комментариев, потом цикл для вывода второго уровня комментариев и т.д. Если вложенность большая, то такая система будет создавать неоправданную нагрузку на сервер.

Прежде всего посмотрим, как выглядят деревья Nested Sets, как они организованы и в чем удобство их использования.

На схеме представлено дерево, описанное по всем правилам метода "Вложенных множеств". Квадратами обозначены узлы дерева, синие цифры в верхнем правом и верхнем левом углах узла - уровень и уникальный идентификатор соответственно, а красные цифры в нижних углах - это левый и правый ключ. Именно в этих двух цифрах - левом и правом ключе заложена вся информация о дереве. И если информацию о ключах занести в базу данных, то работа с деревом намного упрощается. Обратите внимание на то, в каком порядке проставлены эти ключи. Если мысленно пройтись по порядку от 1 до 32, то вы обойдете все узлы дерева слева направо. Фактически это путь обхода всех узлов дерева слева направо.

При использовании такой структуры дерева каталогов, очень сильно упрощается выборка определенных элементов, таких как родительская ветка, подчиненные узлы, вообще вся "ветка" в которой участвует наш узел.

Для того чтобы использовать алгоритм NestedSets, в таблицу с комментариями, необходимо добавить поляlevel, leftkey иrightkey.

Модуль ORM-MPTT

После скачивания и установки данного модуля в папку modules, данный модуль необходимо подключить в файле bootrstrap.php.

Далее, при создании модели, где необходимо получить древовидное дерево комментариев, необходимо использовать модуль ORM_MPTT.

Использование модуля ORM-MPTT. Листинг 28.1

class Model_Comment extends ORM_MPTT {

}

Следующим этапом создадим таблицу с комментариями:

id – уникальный ключ записи

statia_id – id статьи, которую мы комментируем

user_id – id пользователя, добавляющего комментарий

body – сам комментарий

parent_id(int)–id комментария-родителя, если комментарий корневой, то parent_id = 0

scope(int) – указание на то, к какой ветке относится данный элемент

lft(int) – левый ключ

rgt (int)– правый ключ

lvl (int)– уровень

Данный модуль можно использовать в любой таблице. Для этого в таблице должны быть вышеперечисленные выделенные столбцы. Нам нужно знать только parent_id, все остальные поля модуль будет заполнять самостоятельно.

Класс модуля содержит множество методов для создания и удаления ветвей и их потомков.

Метод создания:

make_root() - создать корневой узел

Методы вставки:

insert_as_first_child() - вставить первый потомок

insert_as_last_child() - вставить последний потомок

insert_as_prev_sibling() - вставить перед братом

insert_as_next_sibling() - вставить после брата

Метод удаления:

delete() - удалить текущий узел и всех потомков

Методы перемещения:

move_to_first_child() – переместить на место первого потомка

move_to_last_child() – переместить на место последнего потомка

move_to_prev_sibling() – переместить перед братом

move_to_next_sibling() – переместить после брата

Методы для получения данных:

root() - корневой узел

roots() - корневые узлы

parent() - родитель

parents() - родители

children() - потомки

fulltree() - полное дерево

siblings() - братья

leaves() - ветки текущего узла

size() - размер текущего узла

count() - число потомков текущего узла

has_children() - имеет ли потомков

is_child() - является ли потомком

is_parent() - является ли родителем

is_sibling() - является ли братом

is_root() - является ли корневым узлом

Этапы разработки:

  1. Создание таблицы usercomments:

  2. Создание модуля usercomment. Помимо вызова ORM_MPTT, мы создали правило not_empty для столбца body и назначили ему лэйбл “комментарий”.

    Создания модуля для древовидного дерева комментариев. Листинг 28.2

    class Model_Usercomment extends ORM_MPTT {

    public function rules()

    {

    return array(

    'body' => array(

    array('not_empty'),

    ),

    );

    }

    public function labels()

    {

    return array(

    'body' => 'Комментарий',

    );

    }

    }

  3. Экшн добавления корневого комментария.

Давайте создадим тестовый экшн, который, при обновлении страницы, будет добавлять корневой комментарий.

Добавление корневого комментария при помощи ORM-MPTT. Листинг 28.3

public function action_userone() {

$cat = ORM::factory('usercomment');

$cat->body = "текст корневого комментария test";

$cat->user_id = $this->user->id;

$cat->make_root();

}

Теперь, если мы выполним данный экшн, то получим следующую запись в таблицу usercomments:

  1. Экшн добавления вложенного комментария

Добавление вложенного комментария при помощи ORM-MPTT. Листинг 28.4

public function action_userone() {

$cat = ORM::factory('usercomment');

$cat->body = "текст корневого комментария test";

$cat->user_id = $this->user->id;

$cat->insert_as_last_child(3);

}

При выполнении данного экшна добавится еще одна запись в таблицу базы данных:

Таким образом, мы прокомментировали комментарий с id = 3.

  1. Создание массива комментариев в экшне

    Массив со вложенными комментариями. Листинг 28.5

    $cat = ORM::factory('usercomment');

    $cat = $cat->fulltree()->as_array();

  2. Вывод массива в шаблоне. Мы можем использовать столбец lvl (level) для формирования отступов.

Вывод вложенных комментариев. Листинг 28.6

<div class="alert">

<?foreach ($comment as $cat):?>

<?

if($cat->lvl > 1) {

$rep = str_repeat('-', 2 * $cat->lvl);

} else {

$rep = NULL;

}

?>

<?=$rep . $cat->body ?>

<hr />

<?endforeach?>

</div>

Теперь можно создать и форму, например, для добавления категорий любой иерархической вложенности. Форма будет состоять из двух элементов: в первом пишем название новой категории, во втором элементе – выбираем вложенность.

Шаблон формы управления категориями. Листинг 28.7

<?if($errors):?>

<?foreach ($errors as $error):?>

<div class="error"><?=$error?></div>

<?endforeach?>

<?endif?>

<?=Form::open('adminka/catalogs', array('class'=>'bs))?>

<?=Form::input('name', null, array('class'=>'input'))?>

<select name="cat_number" class="span3">

<option value="0">

<Выберете категорию>

</option>

<?foreach ($cat as $cat_one):?>

<option value="<?=$cat_one->id?>">

<?=str_repeat(' • ', 1 * $cat_one->lvl).$cat_one->name?>

</option>

<?endforeach?>

</select>

<?=Form::submit('add','Добавить', array('class'=>'btn))?>

<?=Form::submit('delete','Удалить', array('class'=>'btn btn-danger'))?>

<?=Form::close()?>

А вот и сам обработчик:

Обработка формы добавления либо удаления категорий. Листинг 28.8

public function action_index() {

$cat = ORM::factory('catalog');

$cat_number = Arr::get($_POST, 'cat_number');

if (isset($_POST['add']))

{

$name = Arr::get($_POST, 'name');

$cat->name = $name;

try

{

if (!$cat_number)

{

$cat->make_root();

}

else

{

$cat->insert_as_last_child($cat_number);

}

$this->request->redirect('adminka/catalogs');

}

catch (ORM_Validation_Exception $e)

{

$errors = $e->errors('validation');

}

}

if (isset($_POST['delete']))

{

if ($cat_number)

{

ORM::factory('catalog', $cat_number)->delete();

}

$this->request->redirect('adminka/catalogs');

}

$cat = $cat->fulltree()->as_array();

$content=View::factory('adminka/v_catalogs')

->bind('cat',$cat)

->bind('errors',$errors);

$this->template->block_center=array($content);

}

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

Вот одно из решений данной задачи.

Вывод древовидного списка каталога в шаблоне. Листинг 28.9

<ul class="navigation treeview">

<?foreach ($catalog as $cat):?>

<?if($cat->is_root()):?>

<li>

<a href="#"><?=$cat->root->name?></a>

<?if($cat->has_children()):?>

<ul>

<?foreach($cat->children() as $catchild):?>

<li>

<a href="#"><?=$catchild->name?></a>

<?if($catchild->has_children()):?>

<ul>

<?foreach($catchild->children() as $catchild2):?>

<li>

<a href="#"><?=$catchild2->name?></a>

<?if($catchild2->has_children()):?>

<ul>

<?foreach($catchild2->children() as $catchild3):?>

<li>

<a href="#"><?=$catchild3->name?></a>

</li>

<?endforeach?>

</ul>

<?endif?>

</li>

<?endforeach?>

</ul>

<?endif?>

</li>

<?endforeach?>

</ul>

<?endif?>

</li>

<?endif?>

<?endforeach?>

</ul>

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]