
Reactions
Универсальная система реакций для MODX 3 — лайки, наборы в стиле GitHub, топы и trending на любом объекте


Сортировка и вывод ресурсов, товаров и других объектов по реакциям через leftJoin на модель ReactionAggregate.
Таблица reactions_aggregates хранит предрассчитанные метрики:
| Поле | Описание |
|---|---|
object_class | Класс объекта (modResource, msProduct…) |
object_id | ID объекта |
context | Контекст MODX |
counts | JSON со счётчиками по типам |
likes | Сумма like + up |
dislikes | Сумма dislike + down |
rating | likes − dislikes |
total | Сумма всех реакций |
trending_score | Оценка trending |
[[!pdoResources?
&parents=`0`
&depth=`10`
&limit=`12`
&leftJoin=`{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = 'modResource' AND Aggregate.context = 'web'"}}`
&sortby=`Aggregate.likes`
&sortdir=`DESC`
&tpl=`tpl.article.card`
]]{'!pdoResources' | snippet : [
'parents' => 0,
'depth' => 10,
'limit' => 12,
'leftJoin' => '{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = \'modResource\' AND Aggregate.context = \'web\'"}}',
'sortby' => 'Aggregate.likes',
'sortdir' => 'DESC',
'tpl' => 'tpl.article.card',
]}[[!pdoResources?
&parents=`5`
&leftJoin=`{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = 'modResource'"}}`
&sortby=`Aggregate.rating`
&sortdir=`DESC`
&limit=`10`
]]{'!pdoResources' | snippet : [
'parents' => 5,
'leftJoin' => '{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = \'modResource\'"}}',
'sortby' => 'Aggregate.rating',
'sortdir' => 'DESC',
'limit' => 10,
]}[[!pdoResources?
&parents=`0`
&leftJoin=`{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = 'modResource'"}}`
&sortby=`Aggregate.trending_score`
&sortdir=`DESC`
&limit=`8`
]]{'!pdoResources' | snippet : [
'parents' => 0,
'leftJoin' => '{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = \'modResource\'"}}',
'sortby' => 'Aggregate.trending_score',
'sortdir' => 'DESC',
'limit' => 8,
]}В чанке tpl.article.card:
<h3>[[+pagetitle]]</h3>
<p>👍 [[!ReactionsCount? &object=`[[+id]]` &format=`{LIKES}`]]</p><h3>{$pagetitle}</h3>
<p>👍 {'!ReactionsCount' | snippet : ['object' => $id, 'format' => '{LIKES}']}</p>Или через плейсхолдер:
[[!ReactionsCount? &object=`[[+id]]` &format=`{LIKES}` &toPlaceholder=`likes_[[+id]]`]]
<span>[[+likes_[[+id]]]]</span>{'!ReactionsCount' | snippet : [
'object' => $id,
'format' => '{LIKES}',
'toPlaceholder' => ('likes_' ~ $id),
]}
<span>{$_modx->getPlaceholder('likes_' ~ $id)}</span>[[!pdoPage?
&element=`pdoResources`
&parents=`0`
&leftJoin=`{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = 'modResource'"}}`
&sortby=`Aggregate.likes`
&sortdir=`DESC`
&tpl=`tpl.article.card`
]]
[[!+page.nav]]{'!pdoPage' | snippet : [
'element' => 'pdoResources',
'parents' => 0,
'leftJoin' => '{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = \'modResource\'"}}',
'sortby' => 'Aggregate.likes',
'sortdir' => 'DESC',
'tpl' => 'tpl.article.card',
]}
{$_modx->getPlaceholder('page.nav')}В Fenom-шаблоне строки (если результаты в $results):
{foreach $results as $row}
<article>
<h2><a href="{$row.id | url}">{$row.pagetitle}</a></h2>
{'!ReactionsCount' | snippet : [
'object' => $row.id,
'format' => '👍 {LIKES}',
]}
</article>
{/foreach}[[!pdoResources?
&parents=`0`
&leftJoin=`{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = 'modResource'"}}`
&where=`{"Aggregate.likes:>":0}`
&sortby=`Aggregate.likes`
&sortdir=`DESC`
]]{'!pdoResources' | snippet : [
'parents' => 0,
'leftJoin' => '{"Aggregate":{"class":"Reactions\\Model\\ReactionAggregate","on":"Aggregate.object_id = modResource.id AND Aggregate.object_class = \'modResource\'"}}',
'where' => '{"Aggregate.likes:>":0}',
'sortby' => 'Aggregate.likes',
'sortdir' => 'DESC',
]}Reactions зарегистрирован в xPDO: класс ReactionAggregate доступен по полному имени Reactions\Model\ReactionAggregate.leftJoin вернёт NULL — такие записи окажутся в конце при DESC или в начале при ASC.php core/components/reactions/cli.php recount.