vorst.ru - Статьи о задачах возникающих при создании сайта и их решении
Статьи из рубрики nested set

Виджет

выбор всех статей по рубрике

В конце предыдущей статьи были указаны две вещи, которых не хватает для начала использования рубрикатора.

Первая из них - необходимо добавить поле rubric в таблицу post. С этой операцией связано и изменения в модели common/models/Post, которые очевидно касаются методов rules() и attributeLabels().

Кроме этого нужно добавить обработку выбранной пользователем рубрики в actionIndex() контроллера frontend/controllers/PostController.


Контроллер

элементарные, но достаточные действия над деревом Nested Set

Все необходимые методы, для управления деревом Nested Set уже определены в расширении.

Поэтому изменения в стандартном контроллере совсем небольшие. Нужно, например, вместо стандартного метода save() модели использовать метод prependTo() расширения, а вместо delete(), deleteWithChildren().

Связано это, конечно же, с необходимостью заполнять или изменять значения в полях lft, rgt, смысл которых рассматривался в первой статье, посвященной организации рубрикатора.

Но, по порядку.


Ввод рубрики

Редактирование, удаление узлов в дереве Nested Set

Мы, в общих чертах, разобрались с деревьями Nested Set и создали модель для хранения дерева. В модели определили пару простых методов.

Теперь нужно разобраться с тем, как добавлять новые рубрики. Начнем с представлений.

Для добавления рубрики нужно определить, в какую родительскую рубрику будет входить новая рубрика. Этой цели служит поле $parent_node модели.

Посмотрим, как будет выглядеть добавление.


Nested Set

Подключение расширения

Мы получили общее представление о древовидной структуре данных называемой Nested Set. Теперь пришло время подключить к блогу соответствующее расширение выполненное в виде поведения.

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

Наши задачи проще и сводятся к следующему:

  1. Управление рубрикатором - добавление и удаление рубрик, редактирование, отображение списка рубрик для визуального контроля.
  2. Закрепление статьи за рубрикой.
  3. Вывод списка рубрик для выбора нужной рубрики.

Для их решения, как обычно, нужна модель, контроллер, представления и еще виджет.


Рубрикатор

как устроена структура данных Nested Set

Рубрикатор, как средство поиска интересных статей, имеет свои недостатки - трудно придумать иерархию, названия рубрик, трудно потом решить к какой конкретно рубрике относится статья. "Но, так принято" - скажите вы и будете правы.

Поэтому, давайте делать рубрикатор.

У нас есть рубрики записанные в правой колонке:

id lft rgt level name
1 1 12 1 Root
2 2 5 2 --- Data structures
3 3 4 3 ------ Nested Set
4 6 9 2 --- Searching
5 7 8 3 ------ Posts chains
6 10 11 2 --- About sites

И нужна структура данных, которая помогла бы нам:

  1. Выводить список в заданном порядке.
  2. Делать отступ при выводе названия рубрик.
  3. Находить все под-рубрики выбранной рубрики, чтобы выводить соответствующие статьи.


Поиск



Виджет

выбор всех статей по рубрике

В конце предыдущей статьи были указаны две вещи, которых не хватает для начала использования рубрикатора.

Первая из них - необходимо добавить поле rubric в таблицу post. С этой операцией связано и изменения в модели common/models/Post, которые очевидно касаются методов rules() и attributeLabels().

Кроме этого нужно добавить обработку выбранной пользователем рубрики в actionIndex() контроллера frontend/controllers/PostController.


    Поделиться

Добавим дополнительное условие выбора статей. Конечно же нужно включить статьи не только выбранной рубрики, но и ее потомков.

public function actionIndex($tag = null, $rubric = null) {
  if($tag)
    $query->andWhere(['like', 'tags', $tag]); // tags LIKE "%$tag%"
  if($rubric) {
    // the selected rubric and all it's sub rubric
    $selectedRubric = Rubric::findOne(['id' => $rubric]);
    $a = []; $a[] = $rubric;
    foreach($selectedRubric->children()->all() as $child)
      $a[] = $child->id;
    $query->andWhere(['in', 'rubric', $a]); // rubric IN ($a)
  }
...

Теперь собственно виджет common/widgets/RubricTree.php. Виджет должен вывести дерево рубрик. Кроме названия будем выводить количество статей в рубрике. Вот тут и пригодилось поле $post_count модели common/models/Rubric упомянутое во второй статье данного цикла.

class RubricTree extends Widget {
  ...
  public function getBranches() {
    $rubrics = \common\models\Rubric::find()
      ->select('id, name, level')
      ->orderBy('lft asc')
      ->all();
    foreach($rubrics as $rubric)
      $rubric->post_count = \common\models\Post::find()
        ->where(['rubric' => $rubric->id])
        ->count();
    return $rubrics;
  }
  public function run() {
    echo $this->render('rubricTree', [
      'title' => $this->title,
      'rubrics' => $this->getBranches(),
    ]);
  }
}

Представление common/widgets/views/rubricTree.php должно вывести заголовок виджета и дерево рубрик используя метод getPrettyName() модели common/models/Rubric. Например так:

<?php foreach($rubrics as $rubric)
  echo Html::a($rubric->getPrettyName(),
    ['post/index', 'rubric' => $rubric->id]) .
    ($rubric->post_count ? ' ' . $rubric->post_count : '');
?>

Теперь можно проверять работоспособность рубрикатора, добавив новые рубрики и закрепив за ними статьи.

Заключение

Мы рассмотрели методы из расширения Nested Set, позволяющие управлять небольшими деревьями. При росте дерева могут понадобиться и другие методы. Например, перемещение узлов вместе с наследниками, но об этом будет написано, если появится такой опыт.

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

Только авторизованные пользователи могут оставлять комментарии. Пожалуйста авторизуйтесь или пройдите регистрацию.