MODX - Как вывести популярные статьи

Урок, на котором рассмотрим, как с помощью сниппетов pdoTools создать виджет, отображающий на сайте 5 самых популярных статей за последние 7 дней.

Описание виджета "Популярные статьи"

Виджет "Популярные статьи" предназначен для отображения на сайте 5 самых просматриваемых страниц за последние 7 дней.

MODX - Виджет "Популярные статьи"
Виджет популярных статей для сайта на MODX

Визуально данный виджет выполним в виде панели (компонента panel) front-end фреймворка Twitter Bootstrap. Содержимое панели организуем в виде нумерованного списка статей в порядке их убывания по количеству просмотров за неделю.

Необходимые компоненты

Рассматривать создание блока "Самое популярное" будем для сайта, в котором статьи организованы на базе компонента Tickets. По умолчанию просмотры тикетов в компоненте Tickets ведутся только для зарегистрированных пользователей. Для того чтобы просмотры регистрировались и для незарегистрированных посетителей, необходимо включить опцию "tickets.count_guests" в настройках.

MODX - Включение параметра "Считать просмотры страниц гостями"
Параметр Ticket, позволяющий учитывать просмотры страниц гостями

Кроме компонента Tickets, потребуется ещё компонент pdoTools. Данный компонент, содержит хороший набор сниппетов. В этой статье будем использовать из этого набора только сниппет pdoResources.

Таблицы TicketView и Ticket

Для создания запросов понадобятся 2 таблицы: TicketView и Ticket.

Первая таблица (TicketView) содержит записи о просмотренных пользователями тикетах. Она состоит из четырёх полей, но для выполнения запроса понадобятся только 2. Первое поле - это parent, оно хранит id просмотренного пользователем тикета. Второе поле - это timestamp, оно предназначено для хранения даты просмотра.

Вторая таблица (Ticket) будет основной. Она понадобится для того чтобы выбрать из неё необходимые ресурсы для которых будем подсчитывать количество просмотров.

Взаимосвязь между таблицами представим на следующей схеме:

MODX - Схема взаимосвязи таблиц Ticket и TicketView
MODX - Схема взаимосвязи таблиц Ticket и TicketView

Разработка виджета "Популярные статьи"

Создание виджета осуществим за 2 шага:

  1. Напишем сниппет getDateWeekAgo, который будет возвращать дату, которая была 7 дней назад.
  2. Выберем и выведем необходимые ресурсы с помощью сниппета pdoResources и getTicket. Сравним время и ресурсы, затрачиваемые ими на обработку.

Создание сниппета getDateWeekAgo

Для создания сниппетов необходимо открыть в левой панели администрирования вкладку "Элементы" и нажать на значок "+" напротив надписи "Сниппеты". В открывшейся странице ввести следующее:

  • в поле "Имя": getDateWeekAgo.
  • в поле "Код сниппета" следующий код:
    <?php
    $formatDate = date('Y-m-d H:i:s');
    $date = new DateTime($formatDate);
    $date->modify("-7 day");
    return $date->format('Y-m-d H:i:s');
MODX - Создание сниппета getDateWeekAgo
MODX - Создание сниппета getDateWeekAgo

Выборка записей с помощью сниппета pdoResources

В качестве 1 способа выберем и выведем записи с помощью сниппета pdoResources. Для этого необходимо открыть нужный шаблон или чанк, и поместить в него следующий код:

<section class="panel panel-danger">
  <div class="panel-heading">
    <h5 class="panel-title">
      <i class="fa fa-heart"></i> <span title="Самые популярные статьи и уроки за последние 7 дней">Популярные статьи</span>
    </h5>
  </div>
  <div class="panel-body">
    [[!pdoResources?
      &loadModels=`tickets`
      &class=`Ticket`
      &leftJoin=`{
        "TicketView": {
          "class": "TicketView",
          "on": "Ticket.id = TicketView.parent"
        }
      }`
      &parents=`4`
      &where=`{"TicketView.timestamp:>":"[[!getDateWeekAgo]]"}`
      &select=`{
        "Ticket": "Ticket.id as id, Ticket.uri as uri, Ticket.pageTitle as pagetitle",
        "TicketView": "COUNT(TicketView.parent) as countviews"
      }`
      &hideContainers=`1`
      &groupby=`Ticket.id`
      &sortby=`{"countviews":"desc"}`
      &tpl=`@INLINE <p><span class="badge" style="background-color:#cc2929">[[+idx]]</span> <a href="[[+uri]]" style="color: #cc2929; vertical-align: middle;">[[+pagetitle]]</a> <span style="color: #cc2929;"><i class="fa fa-eye" style="vertical-align: middle;"></i><span style="vertical-align: middle;" title="Количество просмотров за последние 7 дней"> [[+countviews]]</span></span></p>`
      &limit=`5`
    ]]  
  </div>
</section>  
MODX - Лог работы pdoResources
MODX - Лог работы pdoResources

Выборка записей с помощью сниппета getTickets

В качестве 2 способа рассмотрим выборку данных и их отображение с помощью сниппета getTickets.

<section class="panel panel-danger">
  <div class="panel-heading">
    <h5 class="panel-title">
      <i class="fa fa-heart"></i> <span title="Самые популярные статьи и уроки за последние 7 дней">Популярные статьи</span>
    </h5>
  </div>
  <div class="panel-body">
    [[!getTickets?
      &leftJoin=`{
        "TicketView": {
          "class": "TicketView",
          "on": "Ticket.id = TicketView.parent"
        }
      }`
      &parents=`4`
      &where=`{"TicketView.timestamp:>":"[[!getDateWeekAgo]]"}`
      &select=`{
        "Ticket": "Ticket.id as id, Ticket.uri as uri, Ticket.pageTitle as pagetitle",
        "TicketView": "COUNT(TicketView.parent) as countviews"
      }`
      &hideContainers=`1`
      &groupby=`Ticket.id`
      &sortby=`{"countviews":"desc"}`
      &tpl=`@INLINE <p><span class="badge" style="background-color:#cc2929">[[+idx]]</span> <a href="[[+uri]]" style="color: #cc2929; vertical-align: middle;">[[+pagetitle]]</a> <span style="color: #cc2929;"><i class="fa fa-eye" style="vertical-align: middle;"></i><span style="vertical-align: middle;" title="Количество просмотров за последние 7 дней"> [[+countviews]]</span></span></p>`
      &limit=`5`
    ]]  
  </div>
</section>  
MODX - Лог работы getTickets
MODX - Лог работы getTickets
Поиск нужных ресурсов вышепредставленные сниппеты осуществляют с помощью параметра &parent. Т.е. для того чтобы настроить виджет "Популярные статьи" на свой сайт, Вам достаточно изменить значение 4 на необходимое.

Сравнение результатов работы сниппетов

Как видно из результатов логов, сниппет pdoResources выбирает данные намного быстрее. Это связано с тем, что в запросе участвуют только нужные таблицы. В то время как сниппет getTickets дополнительно подключает ещё классы (таблицы) TicketsSection, modUser и modUserProfile.



   MODX Revo 0    2162 0

Комментарии (21)

  1. Алексей # 0
    Шеф, ты как в воду глядел) Я как раз хотел спросить как реализовать популярные статьи)
    А если нужны вообще популярные, а не за последние 7 дней. То просто делать без сниппета и из кода убрать
    &where=`{"TicketView.timestamp:>":"[[!getDateWeekAgo]]"}`
    ?
    1. Александр Мальцев # 0
      Логично :)
      А зачем спрашиваешь? Если ты сам ответил на свой вопрос.
      1. Алексей # 0
        Шеф, это был уточняющий вопрос, Я не был уверен в своём ответе) Сейчас только буду приступать делать, решил заранее подстраховаться) Видишь, твои уроки не проходят даром.
        Два вопроса не по теме)
        Ты вообще никаким редактором в админке не пользуешься?
        Водяные знаки к картинкам делаешь в каком-то онлайн сервисе или как-то в модексе это дело автоматизировал?
        1. Александр Мальцев # 0
          Предпочитаю чистый HTML код. Поэтому из редакторов использую только Ace.
          Картинки делаю в GIMP, а именно их обрезку, настройку (если необходимо), наложение водяных знаков и сжатие. Там всё это делается за несколько кликов. В автоматизации данного процесса с помощью php не вижу смысла, т.к. не делаю больше 10 картинок в неделю.
          1. Алексей # 0
            Для чистого кода MarkitUp очень удобен, он чисто теги вставляет. А кроме того в настройках можно заточить теги под Markdown. Но Тексетс не хочет его подхватывать. Так бы было вообще шикарно. Попробуй на досуге MarkitUp для редактирования ресурсов.
            1. Александр Мальцев # 0
              Знаю что такое MarkitUp. Но, Ace нравится больше.
    2. Аня # 0
      Привет Александр.
      Есть вопрос. Заметила, что у modx revo картинки индексируются как отдельная страница.
      Это как то влияет на продвижение в поисковиках? Ведь общее количество загруженных страниц получается явно больше, за счет индексации изображений. Есть ли санкции у писковиков для modx за это?))
      1. Александр Мальцев # 0
        Привет.
        Что-то новенькое )
        У меня количество страниц равно количеству загруженных страниц.
      2. Алексей # 0
        Шеф, вопрос для саморазвития)
        Таблицу TicketView нашел в бд, а вот вторую таблицу Ticket, которая основная, не могу найти. Откуда вообще такие названия, если в бд они по другому называются? А то не пойму как работают leftJoin и select с такими названиями таблиц)
        1. Александр Мальцев # 0
          Это не совсем таблицы, это название классов.
          Для компонента Tickets они берутся из файла tickets.mysql.schema.xml.
          Он находится в каталоге /core/components/tickets/model/schema.
          Если его открыть, то будет видно, что данный класс — это расширение modResource.
          <object class="Ticket" extends="modResource">
          А modResource — это уже стандартный класс MODX. Он берётся уже из файла modx.mysql.schema.xml. Он находится в каталоге /core/model/schema.
          В этом файле видно что с классом modResource связана таблица site_content.
          <object class="modResource" table="site_content" extends="modAccessibleSimpleObject" inherit="single">
          В итоге класс Ticket — это таблица site_content. В статье описано более просто, для того чтобы не вдаваться в подробности. Чтобы было более понятно начинающему разработчику. Т.е. таблица TicketView — просмотры, Ticket — тикеты.
          1. Алексей # 0
            Благодарю! Буду теперь знать что и как)
            Урока по созданию слайдера с использованием FlexSlider, например, стоит ожидать?)
            1. Александр Мальцев # 0
              Да, можно.
        2. Алексей # 0
          Шеф, надо добавить
          &hideContainers=`1`
          А то так разделы/категории попадают в список статей)
          1. Александр Мальцев # 0
            Спасибо, Алексей.
            У меня в примере не было категорий, поэтому этот момент упустил.
            1. Алексей # 0
              Считай меня сектантом, но лучше Благо-дарить, чем желать спасения)
              Для этого у тебя есть такие посетители как Я)))
              Рад помочь хорошему человеку! Благодаря твоему блогу, Я прогрессирую в изучении Модэкса, чему очень рад. Находя такие мелкие недочёты, Я вижу, что Я развиваюсь)
          2. Андрей # 0
            Александр, подскажите пожалуйста, как мне реализовать описанный функционал, если в выборку должны попасть изображения ms2Gallery.
            Делаю так
            [[!getTickets?
            &parents=`7` 
            &depth=`1` 
            &tpl=`tpl.post` 
            &includeContent=`1`
            &limit=`5`
            &hideContainers=`1`
            &loadModels=`ms2gallery`
            &where=`{"TicketView.timestamp:>":"[[!getDateWeekAgo]]"}`
            &leftJoin=`{
            "120x90": {"class":"msResourceFile","alias":"120x90", "on": "120x90.resource_id = Ticket.id AND 120x90.path LIKE '%/120x90/' AND 120x90.rank=0"},
            "TicketView": {"class": "TicketView","on":"Ticket.id = TicketView.parent"}
            }`
            &select=`{"Ticket":"*","120x90":"120x90.url as 120x90" "TicketView": "COUNT(TicketView.parent) as countviews"}`
            &groupby=`Ticket.id`
            &showLog=`0`
            ]]
            Чанк tpl.post
            <div class="media post">
              <div class="pull-left">
            	<img src="[[+120x90]]" alt="nature" class="img-fluid">
              </div>
              <div class="media-body">
            	<a class="post-title" href="blog-single-left">[[+pagetitle]]</a> [[+countviews]]
            	<div class="post-meta">
            	  <a href="#">[[+fullname]]</a> [[+date_ago]]
            	</div>
              </div>
            </div>
            В выводе не отображаются countviews и изображения. Спасибо
            1. Андрей # 0
              Увидел сам. Запятую в селекте не поставил, поэтому вывод не тот был
            2. Алексей # 0
              Здравствуйте.
              Сделал как у вас — все работает, спасибо.
              Но вот такая проблема (с данной статьей не совсем связанная), — количество просмотров, как будто закешировалось (вывожу [[+views]]. После просмотра новых тикетов показывается (после перезагрузки страницы) один просмотр, последующие визиты на счетчике не отражаются. В настройках tickets «Считать просмотры страниц гостями — да». Количество комментариев отображается корректно, а просмотры не считает.
              Подскажите в чем причина.
              1. Александр Мальцев # 0
                В tickets для подсчёта просмотра ресурсов незарегистрированными пользователями используется не прямой алгоритм, т.е. количество просмотров не увеличится, если пользователь просто перезагрузит страницу. Попробуйте страницу открыть в другом браузере.
                Так же сниппет который извлекает данные из базы должен быть вызван не кэшированным:
                [[!TicketMeta? &tpl=`tpl.Tickets.meta`]]
                
              2. Кирилл # 0
                Ребят вопрос, а как сделать так чтобы я мог выбирать сам статью и выводить ее в разделе, или на странице? Например селектом, и еще (Но это уже совсем круто) чтобы можно было в поле вбивать текст и статья искалась, чтобы не весь список статей вылазил!
                1. Александр Мальцев # 0
                  Можно начать делать так:
                  [[!ajaxpage]]
                  <div id="id1"></div>
                  <div id="id2">
                    <select id="myid">
                      [[!pdoResources?
                        &tpl=`@INLINE <option value="[[+id]]">[[+pagetitle]]</option>`
                        &parents=`13`
                      ]]
                    </select>
                  </div>
                  <button id="mybtn">Получить контент ресурса</button>
                  <script>
                  // после загрузки страницы
                  $(function() {
                    $('#mybtn').click(function(){
                      var id = $('#myid').val().toString(); 
                      $.post(document.location.href, {action: 'showContent', pageid: id}, function(data) {
                        $('#id1').html(data);
                      });
                    return false;
                    });
                  });
                  </script>
                  
                  Т.е. с помощью pdoResources выводим необходимый список страниц. После выбора необходимой страницы и нажатию на кнопку, запускаем ajax-запрос на сервер, который передаёт php скрипту ([[!ajaxpage]]), расположенному на этой же странице, id ресурса, который мы хотим получить. После получения выводим его в блок, имеющий id=«id1».
                  php скрипт [[!ajaxpage]] — это сниппет с именем ajaxpage, который возвращает контент ресурса по его id.
                  Код сниппета ajaxpage будет следующим:
                  <?php
                  if (empty($_SERVER['HTTP_X_REQUESTED_WITH']) || $_SERVER['HTTP_X_REQUESTED_WITH'] != 'XMLHttpRequest') {return;}
                  if (empty($_POST['action'])) {return;}
                  $res = '';
                  switch ($_POST['action']) {
                    case 'showContent':
                      $resource = $modx->getObject('modResource',$_POST['pageid']);
                      $res = $resource->get('content');
                      break;
                  }
                  if (!empty($res)) {
                    die($res);
                  };
                  

                Вы должны авторизоваться, чтобы оставлять комментарии.