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

В этой статье изучим как можно очень просто добавить на сайт, работающим под управлением CMS MODX, подсчет количества просмотров страниц.
Краткий план
Подсчет количества просмотров страниц на сайте организуем так:
- само значение будем хранить в TV поле
views
; - его обновление будем выполнять посредством плагина
UpdateTVViews
; - для получения списка самых просматриваемых ресурсов напишем сниппет
getMostViewedResources
; - для вывода результатов работы сниппета
getMostViewedResources
будем использовать чанки:tplMostViewedResourcesRow
(для одного элемента) иtplMostViewedResources
(в качестве обёртки, для заворачивания всех элементов).
Защиту от накрутки выполним с использованием куки.
В коде шаблона получить количество просмотров ресурса можно будет посредством плейсхолдера [[!+views]]
, который будем устанавливать с помощью плагина UpdateTVViews
.
Вывести список самых популярных ресурсов можно будет посредством вызова сниппета [[!getMostViewedResources]]
.
Шаг 1. Создание TV для хранения просмотров
Разработку в MODX начнем с создания дополнительного поля и его настройки следующим образом:
- на вкладке «Общая информация» в «Название» введём views и в «Подпись» – Количество просмотров;
- на вкладке «Параметры ввода» в выпадающем списке «Тип ввода» выберем Число, а в поле «Номер по умолчанию» введём значение
0
; - на вкладке «Параметры вывода» в выпадающем списке «Тип ввода» выберем значение По умолчанию;
- на вкладке «Доступно для шаблонов» отметим галочками шаблоны, для которых это TV должно быть доступно.

После этого в ресурсах, которые используют один из шаблонов для которых это TV доступно, на вкладке «Дополнительные поля (TV)» появится только что созданное 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);
Для того чтобы плагин отслеживал данное событие, его необходимо включить на вкладке «Системные события»:

В коде, приведенном выше, для того чтобы счётчик просмотров не увеличивался при повторном просмотре некоторого ресурса одним и тем же пользователем используются куки. Установка куки выполняется посредством 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.
Добрый день Александр! Подскажите как можно сделать в вашем решении, чтобы статистику можно было переключить на кол-во просмотров только за день (сутки)?
То есть выбрал период: день, и показывает только текущие просмотры ресурсов (за день)Привет! Тут никак, ну если только за текущие сутки. В этом случае нужно просто очищать это TV-поле у всех ресурсов, когда начинается новый день.
Да, так и сделал, очистка вручную, если интересно допустим посмотреть что просматривают за день. Небольшой код:
Где 'tmplvarid' =>, id tv-шки в которой надо обнулить значения
Использовал вот эту статью - ИТ ШефХотел через сниппет, чтобы по кнопке очищалось, но проще через консоль, выполнил когда надо. Может пригодиться кому.
Кстати отличный счетчик, ничего лишнего, все шустро работает, и на примере по изучать одно удовольствие. Спасибо!Пожалуйста! Рад что всё получилось и поделились своим решением.
Спасибо за статью да и вообще за работу сайта itchief — очень много полезной информации.
Вопрос по сабжу — подключил количество просмотров. Но вместо варианта со сниппетом для вывода «популярных статей» использую вывод через pdoResources с сортировкой по TV «views». Сортировка работает. Есть ли большая разница в сортировке через pdoResources и сниппетом указаном в статье?
PS в Modx недавно
Использовать своё решение или сторонний компонент, это вопрос не только MODX, но и любых систем. Своё решение нужно написать, как, например, в этой статье. Это намного дольше и менее универсальнее, чем использовать какой-то инструмент, например, такой как pdoResources. Но за потраченное время вы также получаете преимущества:
1. В производительности. Сниппет в статье предназначен четко для выполнения одной этой задачи. Он будет выполняться максимально быстро. Инструмент, который в данном случае является pdoResources, является универсальным. Он предназначен для выполнения большого количества различных задач. Так как размер его кода намного больше, чем сниппета в этой статье, то и выполняться он будет гораздо дольше.
2. В кастомизации. В сниппете я могу написать что угодно при необходимости, готовый компонент работает в рамках его функционала.
3. В независимости от сторонних решений. Например, если автор pdoResources не выпустит обновления для новой версии MODX, то вы не сможете обновиться. Или, например вы хотите перейти на новую версию PHP, и сама MODX с ней совместима, а этот компонент нет.
Такой вопрос в тикете выводится количество просмотров, а как вывести в раздел с тикетами там не работает, переделал как вы сделали новый плагин viewsResource,
Заметил что у вас на сайте тоже странный подсчет, в разделе с тикетами 1.7K а если зайти в статью то там 101
Такой вопрос в тикете выводится количество просмотров, а как вывести в раздел с тикетами там не работает, переделал как вы сделали новый плагин viewsResource,
Заметил что у вас на сайте тоже странный подсчет, в разделе с тикетами 1.7K а если зайти в статью то там 101
Но возникла проблема.
MODX «закалена», папка CORE вынесена за корневую директорию.
А после установки HitsPage в корне система создает вторую папку CORE с файлами HitsPage.
Как установить компонент вне корня?
А есть код который просто бы учитывал просмотр определенной ссылки на странице сайта с несколькими такими же, но разными ссылками и количество выводилось бы отдельно каждой ссылки через html код?
Подскажите пожалуйста.
Наиболее простой вариант – это добавить к ссылкам utm метки. Если нужно просто статистика, то можно это выполнять через Google Analytics. Если нужно выводить на странице, то тоже можно попробовать получать эти данные из этого сервиса.
Ну или пойти по обычному пути, а именно написать свой компонент для MODX, решающий эту задачу. Если для хранения данных подойдет TV, то можете выполнить это через них.
В этом случае необходимо немного доработать сниппет topTicketsView:
Теперь при вызове сниппета нам доступен параметр parent. Если его не устанавливать, то выборка не будет ограничиваться. А если ему указать id категории, то выбор тикетов будет ограничиваться ей.
2. Создал плагин
3. в Чанк «Footer» добавил
[[!+viewsTicket]]
[[!+viewsSection]]
[[*views]]
В результате видно не меняющуюся цифру «0» от этого [[*views]]
Если вам необходимо организовать плагин для обычных ресурсов (modDocument), то вам нужно вместо Ticket использовать modDocument:
Лучше не использовать [[*views]], т.к. он создаст дополнительные запросы к базе данных и увеличит время парсинга страницы. Используйте подготовленный плейсхолдер [[!+viewsTicket]].
// если тип ресурса == modDocument
if ($class_key == 'modDocument') это исправил.
[[*views]] убрал, сразу время уменьшилось.
но это [[!+viewsTicket]] не отображается, может еще что поменять или весь плагин другой нужно?
Имя плейсхолдера изменил на более логичное viewsResource.
В чанк «Footer» добавил [[!+viewsResource]].
На странице не отображается, даже 0 и ноль не меняется в менеджере ресурса.
Не могу никак понять как в getPlaceholder вывести значение именно той новости что нужна
вывод идёт через pdotools, используется collection
в чанке указываю так
itchief.ru/assets/uploadify/1/b/8/1b88e5cd21a6af93445a6ac206dcd79a.png
Всё работает то что вы написали
просто кроме $id_tv = 2; надо было поменять $resource->setTVValue(2,$views_resource); и тут
2 на нужный id tv