Создание счетчика посещений для сайта на MODX

Александр Мальцев
Александр Мальцев
10K
21
Создание счетчика посещений для сайта на MODX
Содержание:
  1. Краткий план
  2. Шаг 1. Создание TV для хранения просмотров
  3. Шаг 2. Создание плагина для обновления TV
  4. Шаг 3. Создание сниппета для вывода самых популярных ресурсов
  5. Комментарии

В этой статье изучим как можно очень просто добавить на сайт, работающим под управлением CMS MODX, подсчет количества просмотров страниц.

Краткий план

Подсчет количества просмотров страниц на сайте организуем так:

  • само значение будем хранить в TV поле views;
  • его обновление будем выполнять посредством плагина UpdateTVViews;
  • для получения списка самых просматриваемых ресурсов напишем сниппет getMostViewedResources;
  • для вывода результатов работы сниппета getMostViewedResources будем использовать чанки: tplMostViewedResourcesRow (для одного элемента) и tplMostViewedResources (в качестве обёртки, для заворачивания всех элементов).

Защиту от накрутки выполним с использованием куки.

В коде шаблона получить количество просмотров ресурса можно будет посредством плейсхолдера [[!+views]], который будем устанавливать с помощью плагина UpdateTVViews.

Вывести список самых популярных ресурсов можно будет посредством вызова сниппета [[!getMostViewedResources]].

Шаг 1. Создание TV для хранения просмотров

Разработку в MODX начнем с создания дополнительного поля и его настройки следующим образом:

  • на вкладке «Общая информация» в «Название» введём views и в «Подпись» – Количество просмотров;
  • на вкладке «Параметры ввода» в выпадающем списке «Тип ввода» выберем Число, а в поле «Номер по умолчанию» введём значение 0;
  • на вкладке «Параметры вывода» в выпадающем списке «Тип ввода» выберем значение По умолчанию;
  • на вкладке «Доступно для шаблонов» отметим галочками шаблоны, для которых это TV должно быть доступно.
Создание дополнительного поля views в MODX

После этого в ресурсах, которые используют один из шаблонов для которых это TV доступно, на вкладке «Дополнительные поля (TV)» появится только что созданное TV-поле views.

Отображение TV поля views в форме создания ресурса

Шаг 2. Создание плагина для обновления TV

После добавления TV в MODX создадим новый плагин и дадим ему название UpdateTVViews. Он будет использоваться для увеличения счетчика просмотров views.

Плагин будет выполняться при наступлении системного события OnLoadWebDocument, которое будет возникать после загрузки документа, но до обработки парсером тегов MODX. Получение текущего ресурса при наступлении этого события можно получить посредством $modx->resource.

Код плагина UpdateTVViews:

<?php
if ($modx->event->name != 'OnLoadWebDocument') {
  return;
}

// получим ресурс
$resource = $modx->resource;
// URL
$url = $modx->makeUrl($resource->get('id'), '', '', 'abs');
// id TV views
$tvViewsId = 1;
// количество просмотров
$views = 0;

// если TV не доступна для этого шаблона, то завершаем
$tvViewsTemplate = $modx->getObject('modTemplateVarTemplate', [
  'tmplvarid' => $tvViewsId,
  'templateid' => $resource->get('template')
]);
if (!$tvViewsTemplate) {
  return;
}

// получаем TV
$tvViews = $modx->getObject('modTemplateVarResource', [
  'tmplvarid' => $tvViewsId,
  'contentid' => $resource->get('id')
]);

// если имеется TV то получаем его значение, иначе создаем
if ($tvViews) {
  $views = $tvViews->get('value');
} else {
  $tvViews = $modx->newObject('modTemplateVarResource');
  // устанавливаем значение поля tmplvarid
  $tvViews->set('tmplvarid', $tvViewsId);
  // устанавливаем значение поля contentid
  $tvViews->set('contentid', $resource->get('id'));
}

// если значение куки не равно текущему URL, то увеличиваем счетчик
if ($_COOKIE['views'] != $url) {
  // устанавливаем количество просмотров
  $tvViews->set('value', ++$views);
  // сохраняем TV
  $tvViews->save();
  // устанавливаем COOKIE views
  setcookie('views', $url, time() + (86400 * 365), $url);
}

// устанавливаем плейсхолдер views
$modx->setPlaceholder('views', $views);

Для того чтобы плагин отслеживал данное событие, его необходимо включить на вкладке «Системные события»:

Установка событий, которые должен отслеживать плагин UpdateTVViews

В коде, приведенном выше, для того чтобы счётчик просмотров не увеличивался при повторном просмотре некоторого ресурса одним и тем же пользователем используются куки. Установка куки выполняется посредством php-функции setcookie, а её получение с помощью ассоциативного массива $_COOKIE.

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

[[!+views]]

Шаг 3. Создание сниппета для вывода самых популярных ресурсов

В завершении напишем сниппет для вывода самых популярных страниц на сайте. Назовем его getMostViewedResources. Сниппет будет получать из базы 5 ресурсов, которые имеют самые большие значения views.

Код сниппета getMostViewedResources:

// id TV views
$tvViewsId = 1;

// создадим новый запрос, используя объект modTemplateVarResource
$query = $modx->newQuery('modTemplateVarResource');
// присоединяем к нему объект modResource
$query->innerJoin('modResource', 'Resource');
// добавляем к запросу условие
$query->where(['modTemplateVarResource.tmplvarid' => $tvViewsId]);
// выполним сортировку по TV (для преобразования строки в беззнаковое число используем mysql функцию CAST)
$query->sortby('CAST(modTemplateVarResource.value as unsigned)', 'DESC');
// укажем поля для выборки
$query->select([
  'Resource.id as id',
  'Resource.pagetitle as pagetitle',
  'modTemplateVarResource.value as countviews'
]);
// ограничим выборку 5 записями
$query->limit(5);
// подготовим и выполним запрос
$query->prepare();
$query->stmt->execute();
$rows = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
// присвоим переменной $output пустую строку
$output = '';
// переберём все записи
foreach ($rows as $row) {
  $row['url'] = $modx->makeUrl($row['id'], '', '', 'abs');
  // оформим вывод строки с использованием чанка tplMostViewedResourcesRow
  $output .= $modx->getChunk('tplMostViewedResourcesRow', $row);
}
$output = $modx->getChunk('tplMostViewedResources', ['output' => $output]);
// вернём результат
return $output;

HTML-шаблон который используется для формирования одной строчки хранится в чанке tplMostViewedResourcesRow:

<li class="list-group-item d-flex justify-content-between align-items-start">
  <div class="ms-2 me-auto">
    <div class="fw-bold"><a href="[[+url]]">[[+pagetitle]]</a></div>
  </div>
  <span class="badge bg-primary rounded-pill">[[+countviews]]</span>
</li>

Чанк tplMostViewedResources используется в качестве обёртки и имеет следующий код:

<div class="card">
  <div class="card-header">Популярные статьи</div>
  <ol class="list-group list-group-flush list-group-numbered">[[+output]]</ol>
</div>

Для вывода блока с самыми популярными страницами необходимо поместить в необходимое место шаблона вызов сниппета getMostViewedResources:

[[!getMostViewedResources]]

Вид блока «Популярные статьи»:

Блок для сайта с самыми просматриваемыми страницами на ресурсе

Оформление этого блока выполнена с использованием Bootstrap 5, а для создания структуры этого списка был выбран компонент из этого фронтенд инструмента под названием List group.

Комментарии:

  1. Y2507
    Y2507
    19.12.2021, 14:38
    Добрый день!
    Такой вопрос в тикете выводится количество просмотров, а как вывести в раздел с тикетами там не работает, переделал как вы сделали новый плагин viewsResource,

    Заметил что у вас на сайте тоже странный подсчет, в разделе с тикетами 1.7K а если зайти в статью то там 101
    1. Y2507
      Y2507
      19.12.2021, 14:37
      Добрый день!
      Такой вопрос в тикете выводится количество просмотров, а как вывести в раздел с тикетами там не работает, переделал как вы сделали новый плагин viewsResource,

      Заметил что у вас на сайте тоже странный подсчет, в разделе с тикетами 1.7K а если зайти в статью то там 101
      1. Олег
        Олег
        08.08.2020, 23:25
        Александр, установил HitsPage, все ОК.
        Но возникла проблема.
        MODX «закалена», папка CORE вынесена за корневую директорию.
        А после установки HitsPage в корне система создает вторую папку CORE с файлами HitsPage.
        Как установить компонент вне корня?
        1. Александр Мальцев
          Александр Мальцев
          10.08.2020, 13:06
          Скорее всего при установке компонента путь к папке core прописан «жёстко». Нужно чтобы разработчик компонента поправил путь к папке core так, чтобы он брался из константы MODX_CORE_PATH.
        2. Дмитрий
          Дмитрий
          20.04.2020, 22:29
          Александр, здравствуйте!
          А есть код который просто бы учитывал просмотр определенной ссылки на странице сайта с несколькими такими же, но разными ссылками и количество выводилось бы отдельно каждой ссылки через html код?
          Подскажите пожалуйста.
          1. Александр Мальцев
            Александр Мальцев
            24.04.2020, 15:12
            Здравствуйте!
            Наиболее простой вариант – это добавить к ссылкам utm метки. Если нужно просто статистика, то можно это выполнять через Google Analytics. Если нужно выводить на странице, то тоже можно попробовать получать эти данные из этого сервиса.
            Ну или пойти по обычному пути, а именно написать свой компонент для MODX, решающий эту задачу. Если для хранения данных подойдет TV, то можете выполнить это через них.
            1. Дмитрий
              Дмитрий
              24.04.2020, 21:31
              Благодарю вас! Видимо да, нужно изучить MODX, но пока что я не имею понятия, что это)
              1. Александр Мальцев
                Александр Мальцев
                25.04.2020, 15:37
                Да, если требуется функционал которого нет по умолчанию в MODX и готового компонента для этого нет, то без изучения MODX тут уже не обойтись.
          2. kalisto
            kalisto
            19.04.2018, 19:42
            И у меня не работает счетчик страниц.
            1. Александр
              Александр
              09.03.2017, 12:10
              Добрый день, подсчет заработал, подскажите, пожалуйста, а как вывести популярные статьи из определенной категории?
              1. Александр Мальцев
                Александр Мальцев
                09.03.2017, 15:18
                Добрый день, Александр.
                В этом случае необходимо немного доработать сниппет topTicketsView:
                <?php
                $parent = $modx->getOption('parent', $scriptProperties, 0);
                $output = $modx->cacheManager->get('topticketview-'.$parent);
                if (empty($output)) {
                  $query = $modx->newQuery('modTemplateVarResource');
                  $query->innerJoin('modResource','Resource');
                  $query->where(array(
                    'modTemplateVarResource.tmplvarid'=>'2',
                    'Resource.class_key' => 'Ticket'
                  ));
                  if ($parent != 0) {
                    $query->where(array(
                      'Resource.parent' => $parent
                    ));  
                  }
                  $query->sortby('CAST(modTemplateVarResource.value as unsigned)',DESC);
                  $query->select(array(
                    'Resource.pagetitle as pagetitle',
                    'Resource.uri as uri',
                    'modTemplateVarResource.value as countviews'
                  ));
                  $query->limit(5);
                  $query->prepare();
                  $query->stmt->execute();
                  $rows = $query->stmt->fetchAll(PDO::FETCH_ASSOC);
                  $output = '';
                  $idx = 0;
                  foreach ($rows as $row) {
                    $idx++;
                    $row['idx'] = $idx;
                    $output .= $modx->getChunk('tpl.top.views.tickets',$row);
                  }
                  $modx->cacheManager->set('topticketview-'.$parent,$output,10800);
                }
                return $output;
                
                Теперь при вызове сниппета нам доступен параметр parent. Если его не устанавливать, то выборка не будет ограничиваться. А если ему указать id категории, то выбор тикетов будет ограничиваться ей.
                <!-- Самые популярные ресурсы на сайте -->
                [[!topTicketsView]]
                <!-- Самые популярные ресурсы в секции, имеющей id = 15
                [[!topTicketsView? &parent=`15`]]
                
                1. Александр
                  Александр
                  09.03.2017, 16:38
                  Всё супер! Заработало! Спасибо вам огромное!
              2. Демьян Золин
                Демьян Золин
                28.02.2017, 21:57
                1. Создал TV
                2. Создал плагин
                3. в Чанк «Footer» добавил
                [[!+viewsTicket]]
                [[!+viewsSection]]
                [[*views]]
                В результате видно не меняющуюся цифру «0» от этого [[*views]]
                1. Александр Мальцев
                  Александр Мальцев
                  02.03.2017, 16:36
                  Скорее всего, у вас ресурс не является Ticket.
                  Если вам необходимо организовать плагин для обычных ресурсов (modDocument), то вам нужно вместо Ticket использовать modDocument:
                  // если тип ресурса == modDocument
                  if ($class_key == 'modDocument')
                  
                  Лучше не использовать [[*views]], т.к. он создаст дополнительные запросы к базе данных и увеличит время парсинга страницы. Используйте подготовленный плейсхолдер [[!+viewsTicket]].
                  1. Демьян Золин
                    Демьян Золин
                    03.03.2017, 01:14
                    Верно ресурс не является Ticket.
                    // если тип ресурса == modDocument
                    if ($class_key == 'modDocument') это исправил.
                    [[*views]] убрал, сразу время уменьшилось.
                    но это [[!+viewsTicket]] не отображается, может еще что поменять или весь плагин другой нужно?
                    1. Александр Мальцев
                      Александр Мальцев
                      08.03.2017, 15:29
                      Немного изменил плагин, теперь он предназначен для подсчёта любых ресурсов у которых есть TV поле views (указание какое TV-поле views имеет id осуществляется посредством переменной $id_tv).
                      <?php
                      if ($modx->event->name == 'OnLoadWebDocument') {
                        // id дополнительного поля views
                        $id_tv = 2;
                        // получим ресурс
                        $resource = $modx->resource;
                        // проверить есть ли у ресурса (его шаблона) TV с указанным id
                        $isTV = $modx->getObject('modTemplateVarTemplate',array(
                          'tmplvarid' => $id_tv,
                          'templateid' => $resource->get('template')
                        ));
                        // если TV нет, то завершаем работу
                        if (!is_object($isTV)) {
                          return;
                        }
                        // получим id ресурса
                        $id = $resource->get('id');
                        // получим url
                        $url = $modx->makeUrl($id,'','',-1);
                      
                        // получим tv поле
                        $tv_resource = $modx->getObject('modTemplateVarResource',array(
                          'tmplvarid' => $id_tv,
                          'contentid' => $id
                        ));
                        // получим id главной страницы
                        $id_start = $modx->getOption('site_start');
                        
                        // если не существует COOKIE viewresource и viewmainresource
                        if ((!isset($_COOKIE['viewresource']) && ($id!=$id_start)) || (!isset($_COOKIE['viewmainresource'])) && ($id==$id_start) ) {
                          // если tv поле является объектом
                          if (is_object($tv_resource)) {
                            // получаем его значение
                            $views_resource = $tv_resource->get('value');
                            // увеличиваем количество просмотров на 1
                            $views_resource++;
                            // сохраняем количество просмотров в таблицу      
                            $resource->setTVValue(2, $views_resource);
                          } else {
                            // устанавливаем количество просмотров равным 1
                            $views_resource = 1;
                            // сохраняем
                            $resource->setTVValue(2,$views_resource);
                          }      
                          // отправка COOKIE
                          if ($id==$id_start) {
                            setcookie('viewmainresource', '1',time() + (86400 * 365),'/');       
                          } else {
                            setcookie('viewresource', '1',time() + (86400 * 365),'/'.$url);
                          }
                        } else {
                          $views_resource = $tv_resource->get('value');
                        }
                        // устанавливаем плейсхолдер viewsTicket
                        $modx->setPlaceholder('viewsResource', $views_resource);
                      }
                      
                      Имя плейсхолдера изменил на более логичное viewsResource.
                      1. Максим
                        Максим
                        21.02.2021, 18:12
                        if (!is_object($isTV)) {
                            return;
                          }
                        почему-то вот из-за этого дальше не отрабатывает ничего, но даже если убираем это вроде всё начинает работать, само поле views начинает нормально считаться но на вывод через viewsResource выводиться всегда главной страницы причём счётчик увеличивается каждый раз когда обновляешь страницу прмчём на рандомное значение от 2 до 5.
                        Не могу никак понять как в getPlaceholder вывести значение именно той новости что нужна
                        вывод идёт через pdotools, используется collection
                        		{$_modx->runSnippet('!pdoPage', [
                        			'tpl' => '@FILE chunks/header_news.tpl',
                        			'parents' => 2,
                        			'limit' => '1',
                        			'includeTVs' => 'imageMajor, headindex, views',
                        			'processTVs' => 1,
                        			'where' => '{"headindex":1}'
                        
                        		])}
                        в чанке указываю так
                        {$_pls["tv.views"]} - выводится нужное значение из конкретной новост
                        {$modx->getPlaceholder('viewsResource')} - выводится значение той странице где расположен чанк вывода то есть главной страницы
                        [[+viewsResource]] - тоже самое что и предыдущее 
                        itchief.ru/assets/uploadify/1/b/8/1b88e5cd21a6af93445a6ac206dcd79a.png
                        1. Максим
                          Максим
                          21.02.2021, 18:39
                          В таком варианте хотя бы работает, но через viewsResource выводится значение текущей страницы, даже если поле шаблону не задано, приходится выводить через тв поле views что как я понимаю не очень хорошо
                          <?php
                          if ($modx->event->name == 'OnLoadWebDocument') {
                              // id дополнительного поля views
                              $id_tv = 6;
                              // получим ресурс
                              $resource = $modx->resource;
                              $id = $resource->get('id');
                              // проверить есть ли у ресурса (его шаблона) TV с указанным id
                              $isTV = $modx->getObject('modTemplateVarTemplate',array(
                                  'tmplvarid' => $id_tv,
                                  'templateid' => $id
                              ));
                          
                              // получим url
                              $url = $modx->makeUrl($id,'','',-1);
                          
                              // получим tv поле
                              $tv_resource = $modx->getObject('modTemplateVarResource',array(
                                  'tmplvarid' => $id_tv,
                                  'contentid' => $id
                              ));
                              
                              // получим id главной страницы
                              $id_start = $modx->getOption('site_start');
                          
                              // если не существует COOKIE viewresource и viewmainresource'
                              if (!isset($_COOKIE['viewresource'])) {
                                  // если tv поле является объектом
                                  if (is_object($tv_resource)) {
                                      // получаем его значение
                                      $views_resource = $tv_resource->get('value');
                                      // увеличиваем количество просмотров на 1
                                      $views_resource++;
                                      // сохраняем количество просмотров в таблицу
                                      $tv_resource->set('value',$views_resource);
                                      $tv_resource->save();   
                                  } else {
                                      // устанавливаем количество просмотров равным 1
                                      $views_resource = 100;
                                      // сохраняем
                                      $tv_resource->$tv_resource->get('value');
                                      $tv_resource->save();   
                                  }
                                  // отправка COOKIE
                                  if ($id==$id_start) {
                                      setcookie('viewmainresource', '1',time() + (86400 * 365),'/');
                                  } else {
                                      setcookie('viewresource', '1',time() + (86400 * 365),'/'.$url);
                                  }
                              } else {
                                  $views_resource = $tv_resource->get('value');
                              }
                              // устанавливаем плейсхолдер viewsTicket
                              $modx->setPlaceholder('viewsResource', $views_resource);
                          }
                          1. Максим
                            Максим
                            28.02.2021, 19:11
                            Приношу извинения за выше написанное лучше удалите этот позор нафиг)
                            Всё работает то что вы написали

                            просто кроме $id_tv = 2; надо было поменять $resource->setTVValue(2,$views_resource); и тут
                            2 на нужный id tv
                        2. Демьян Золин
                          Демьян Золин
                          10.03.2017, 15:28
                          В плагин viewsCount поместил вышеприведенный код.
                          В чанк «Footer» добавил [[!+viewsResource]].
                          На странице не отображается, даже 0 и ноль не меняется в менеджере ресурса.
                          1. alex1
                            alex1
                            21.12.2017, 13:19
                            тоже самое сделал, вообще в ошибку 500 падает
                  Войдите, пожалуйста, в аккаунт, чтобы оставить комментарий.