Как выводить комментарии с отступами - vorst.ru

Комментарии


Как выводить комментарии с отступами?

Как вывести комментарии с отступом? Использование рекурсии для вывода комментариев. Расширение для вывода комментариев для статей и любых других моделей.

Комментарии

Как выводить комментарии с отступами?

На сайте оставили комментарий, вы ответили. Оппонент что-то еще уточнил, вы опять ответили. Цепочка из нескольких вопросов/ответов, которую нужно представить в виде лестницы.

  • Подскажите, как организовать комментарии на сайте?
    • Есть варианты. Каковы ваши требования?
      • Необходимо, чтобы юзер мог оставить комментарий, который будет размещен после модерации.
  • Добрый день! У меня такая же проблема.
    • Можно использовать следующий алгоритм ... [Ответить]

В каждом мини-диалоге участвуют двое. Если кто-то еще оставляет комментарий, то это новый мини-диалог. Вклиниться в мини-диалог стороннему комментатору нельзя.


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

Вы администратор сайта и вы видите оставленный комментарий в backend. Вы отвечаете на комментарий.

У пользователя, оставившего комментарий, должен отобразиться ваш ответ во frontend и кнопка [Ответить].

При упомянутом ограничении, а именно - в диалоге участвуют только двое: администратор и комментатор, структура данных для хранения комментариев примерно следующая.

id integer
post_id integer
user_id integer
content text
status integer
last boolean
thread char(32)
created_at integer

Нить диалога

Каждый новый комментарий получает уникальное значение - код нити (thread). Ответный комментарий получает то же значение thread, что и начальный комментарий. Если отсортировать все комментарии с одинаковым значением thread по дате, то получим цепочку диалога.

content thread
Подскажите, как организовать комментарии на сайте? a1
Есть варианты. Каковы ваши требования? a1
Необходимо, чтобы юзер мог оставить комментарий, который будет размещен после модерации. a1
Добрый день! У меня такая же проблема. b2
Можно использовать следующий алгоритм ... b2

Вывод комментариев в backend

Вывод комментариев уступами можно выполнить с помощью стандартного Grid, если сохранить текущее значение thread и использовать его при выводе следующего комментария. Для сохранения используем внутреннюю переменную params класса yii\base\View.

<?= GridView::widget([
  'dataProvider' => $dataProvider,
  'filterModel' => $searchModel,
  'layout' => "{items}\n{summary}\n{pager}",
  'columns' => [
  ...
  [
    'attribute' => 'content',
    'value' => function($data) {
      // declare params for indention in a row,
      // using View class internal variable - params
      if(!isset($this->params['thread']))
        $this->params['thread'] = false;
      if($this->params['thread'] == $data->thread) 
        $this->params['indention'] .= '--';
      else {
        $this->params['thread'] = $data->thread;
        $this->params['indention'] = '';
      }
      return $this->params['indention'] . ' ' . $data->content;
    }
  ],
  ...

Вывод комментариев во frontend

Верстка во frontend, как правило, более изощренная и часто основана на bootstrap media object. Начинается вывод с frontend/views/post/view.php в котором где-то, после текста поста, присутствует следующий код:

<?php echo $this->render('_comments', [
  'post' => $model,
  'comments' => $model->comments,
]); ?>

Комментарии выводятся один за другим. Если у комментариев одинаковый thread, то нужно сделать отступ и напечатать текущий комментарий. И так далее, пока не изменится thread. Рекурсивно подобный процесс представить легче.

Рекурсия

Определим основной цикл вывода frontend/views/post/_comments.php. Как и в backend промежуточные переменные сохраняются во внутренней переменной params класса yii\base\View.

<?php
function close_thread($level) {
  while($level > 1) {
    $level--;
    echo '</div>';
  }
}
?>
<?php $i = 0; $this->params['comments'] = $comments; ?>
<ul class="media-list">
  <?php while($i < count($comments)): ?>
    <li class="media">    
    <?php echo $this->render('_comment', [
      'i' => $i, 
      'thread' => $comments[$i]->thread, 
      'level' => 0]); 
      $i = $this->params['comment_i'];
    ?>
    </li> <!-- .media / -->
  <?php endwhile; ?>
</ul>

При выводе текущего комментария frontend/views/post/_comment.php новый блок (отступ) выводится пока thread тот же, что и у предыдущего комментария. Если же thread изменился, то блоки закрываются.

<?php $comment = $this->params['comments'][$i]; ?>
<div class="media-left">
  <i class="fa fa-2x fa-user"></i>
</div>
<div class="media-body">
  <div class="media-content">
    <h4 class="media-heading"><?= $comment->author->name ?></h4>
    <div class="alert alert-info" role="alert"><?= $comment->content ?></div>
    <?php if($comment->canBeAnswered()): ?>
      <div class="text-right">
        <a href="#comment-area" data-comment-thread="<?= $comment->thread ?>" class="btn btn-info reply-btn">
          <?= \Yii::t('app', 'Reply') ?>
        </a>
      </div>
    <?php endif; ?>
  </div>
<?php 
$i++;  $this->params['comment_i'] = $i; 
if($i < count($this->params['comments'])) {
  $comment = $this->params['comments'][$i];
  if($thread == $comment->thread) {
    $level++;
    echo '<div class="media-body">';
      echo $this->render('_comment', 
        ['i' => $i, 'thread' => $thread, 'level' => $level]);
  } else
    close_thread($level);
} else {
  close_thread($level);
}
?>

Право на ответ

Метод canBeAnswered() модели Comment вызывается во frontend при определении права текущего пользователя отвечать на последний комментарий.

Пользователь имеет право ответить на комментарий, если:

  • Пользователь авторизован;
  • Пользователь является инициатором мини-диалога;
  • Это последний комментарий в мини-диалоге;
  • И последний комментарий принадлежит модератору.


public function canBeAnswered() {
  $countInThread = Comment::find()
    ->where(['thread' => $this->thread, 'user_id' => \Yii::$app->user->id])
    ->count();
  return !\Yii::$app->user->isGuest &&
    $this->last && // this is a last comment in a thread
    \Yii::$app->user->identity->group == User::GROUP_COMMENTATOR && // you are a commentator
    $this->user_id != \Yii::$app->user->id && // last comment not yours
    $countInThread > 0; // you begin this thread
}

Как это работает во frontend, можно посмотреть на сайте. О backend подробнее в расширении sergmoro1/yii2-blog-tools.

Заключение

Комментарии в расширении sergmoro1/yii2-blog-tools могут оставлять только зарегистрированные пользователи. Комментарии должны модерироваться администратором сайта. Диалог возможен только между пользователем и администратором сайта.

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

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