[xLike] Идеальная система лайков с оптимистичным интерфейсом и правильной формулой

Пакет довольно прост и одновременно функционален. Выполняет систему рейтинга для любого объекта, по-умолчанию, для ресурсов. Чем-то напоминает систему лайков на YouTube, в частности, в пакете реализован оптимистичный интерфейс.


Также, особенностью компонента является правильный подсчет рейтинга на основе вычисления нижней границы доверительного интервала Вильсона для параметра Бернулли. По-простому: эта формула не даст встать новым записям с 1 лайком и 0 дизлайками выше более старых записей с сильным рейтингом.

Список преимуществ:
  1. Оптимистичный интерфейс,
  2. Наиболее правильная формула вычисления рейтинга,
  3. Работа с любыми объектами (modResource, modUser, кастомные объекты),
  4. Голосование гостями,
  5. Анимированное изменение полосы/числа рейтинга,
  6. Событие плагина — xLikeOnVote.


Сниппет xLike


Выводит систему рейтинга на месте вызова.

Параметры

tpl — чанк шаблона вывода. По-умолчанию, tpl.xLike.
mode — режим работы сниппета: db или local. В режиме local сниппет обходится без запроса к базе для того, чтобы узнать кол-во лайков/дизлайков и рейтинг, вместо этого вам необходимо передать эти цифры в сниппет в качестве параметров. По-умолчанию, db.
guest — разрешить гостям голосовать. По-умолчанию, Да.
parent — ID объекта. По-умолчанию, ID текущего ресурса.
class — класс объекта. По-умолчанию, modResource.
list — название списка (дополнительный параметр группировки). По-умолчанию, default.

Примеры

Вывод рейтинга для текущего ресурса с возможностью голосовать гостям:
{'!xLike' | snippet}

Вывод рейтинга для текущего тикета без возможности голосовать гостям:
{'!xLike' | snippet : [
    'guest' => false,
    'class' => 'Ticket',
]}

Вывод рейтинга для текущего ресурса в режиме local:
{'!xLike' | snippet : [
    'mode' => 'local',
    'likes' => $_modx->resource['likes'],
    'dislikes' => $_modx->resource['dislikes'],
    'rating' => $_modx->resource['rating'],
]}
Подразумевается, что в ТВ полях likes, dislikes, rating хранится соответствующая информация.

 

Событие xLikeOnVote


Данное событие срабатывает после голосования пользователем и всех проверок.

Параметры плагина

parent — ID объекта, к которому привязан рейтинг,
class — класс объекта,
list — название списка,
likes — кол-во лайков,
dislikes — кол-во дизлайков,
rating — текущий рейтинг, уже пересчитанный.

Практическое применение

Задача: при голосовании в рейтинге (с параметрами сниппета class=modResource, list=default), записывать текущий рейтинг в ТВ поле rating.
Код плагина:
switch ($modx->event->name) {
    case "xLikeOnVote":
        if ($class == 'modResource' && $list == 'default') {
            if ($resource = $modx->getObject($class, array('id' => $parent))) {
                $resource->setTVValue('rating', $rating);
            }
        }
        break;
}
Таким образом, мы сможем сортировать ресурсы (или любые другие объекты) по правильно подсчитанному рейтингу.

Демо-сайт
Скачать в Modstore

Если есть желание отблагодарить:
ЯД — 4100159550314
PayPal — pavelgvozdb@yandex.ru
Павел Гвоздь
16 июля 2017, 18:05
19
3 485
+34

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

Богдан
17 июля 2017, 08:53
0
Отличный компонент, спасибо, Павел! Выглядит красиво и функционально. Единственный минус как по мне — нужно вызывать сниппет, а если это делать это из чанка например pdoResources, увеличится количество запросов к БД и время генерации страницы. Есть ли в планах сделать возможным голосование без вызова сниппета (вручную назначить класс/id для кнопок и полосы рейтинга в чанке pdoResources)?
    Павел Гвоздь
    17 июля 2017, 10:12
    0
    А как вы будете получать рейтинг и кол-во голосов без запросов к БД?
      Руслан Кундиус
      17 июля 2017, 11:06
      +2
      Количество голосов можно из tv поля, или джоином, голоса ведь в бд хранятся? Если правильно проставить js-* классы и data атрибуты должно работать, не понятно только что в propkey попадает, пока не могу «пощупать».
        Павел Гвоздь
        17 июля 2017, 12:18
        +2
        Да, все верно, если сохранять данные рейтинга и кол-ва голосов в полях ресурса или другого объекта, то можно обойтись и без лишних запросов к БД. Вот тут код компонента, а тут непосредственно сниппет, который формирует propkey.
          Богдан
          17 июля 2017, 12:28
          +2
          Отправил «спасибо» на яндекс кошелек. Если будет работать без сниппета — будет вообще круто. Позже посмотрю код, как там все устроено.
        Павел Гвоздь
        17 июля 2017, 12:22
        +2
        На счёт дополнительного режима сниппета без запросов к БД можно подумать, хорошая идея!
        Павел Гвоздь
        17 июля 2017, 14:28
        0
        Единственное, запрос к БД в любом случае будет совершаться, чтобы узнать, голосовал ли юзер уже.
    Павел Гвоздь
    17 июля 2017, 14:50
    +5
    Готово! У сниппета появился дополнительный параметр &mode, позволяет выбрать режим вывода сниппета: db или local. В режиме local сниппет обходится без запроса к базе для того, чтобы узнать кол-во лайков/дизлайков и рейтинг, вместо этого вам будет необходимо передать эти цифры в качестве параметров в сниппет:
    {'!xLike' | snippet : [
        'mode' => 'local',
        'likes' => $_modx->resource['likes'],
        'dislikes' => $_modx->resource['dislikes'],
        'rating' => $_modx->resource['rating'],
    ]}
    Подразумевается, что в ТВ полях likes, dislikes, rating хранится соответствующая информация.
      Alexander Smolenskyi
      09 августа 2017, 14:49
      0
      объясните, куда этот код вообще вставлять и чтобы записывало в ТВ поля.
      Ксения
      09 июня 2018, 17:43
      0
      Объясните пожалуйста, как вот это работает?
      {'!xLike' | snippet : [
          'mode' => 'local',
          'likes' => $_modx->resource['likes'],
          'dislikes' => $_modx->resource['dislikes'],
          'rating' => $_modx->resource['rating'],
      ]}
      В TV записаны значения и они нормально выводятся пользователю.
      Если он «голосует» то в БД попадает соответствующая запись. (НО другому пользователю опять выведется то же значение TV что и было)
      Я переношу систему рейтингов и думала, что к значению в TV будут прибавляться значения из БД, но видимо это не так.
      Чтобы обновлять TV создала плагин
      <?php
      switch ($modx->event->name) {
          case "xLikeOnVote":
              if ($class == 'modResource' && $list == 'default') {
                  if ($resource = $modx->getObject($class, array('id' => $parent))) {
                      $resource->setTVValue('myLike', $rating);
                      $resource->setTVValue('likes', $likes);
                      $resource->setTVValue('dislikes', $dislikes);
                      $resource->save();
                  }
              }
              break;
      }
      но он все предыдущие значения TV обнуляет.

      Так как он должен работать?
      Делать как-то так?
      $resource->setTVValue('likes', $resource->getTVValue('likes') + $likes);
        Павел Гвоздь
        09 июня 2018, 18:50
        0
        Делать как-то так?
        Нет, у вас всё верно в плагине, кроме
        $resource->save();
        , можно без него. MODX при
        $resource->setTVValue()
        уже выполняет сохранение. Поправил в посте.

        Вероятно у вас что-то закешено. Может база, не знаю. У меня работает корректно на демо-сайте.
          Ксения
          09 июня 2018, 19:08
          0
          Очень странно. Все настройки кэширования по умолчанию.
          в tv like стояло 520 стало 2
          в tv dislike стояло 70 стало 0
          то есть просто записались(посчитались) данные из БД, минуя значения в tv
            Павел Гвоздь
            09 июня 2018, 19:16
            0
            А, вот вы о чём. Так да, в плагине в переменных $likes и $dislikes реальные значения лайков и дизов, а не накрученные.
            Павел Гвоздь
            09 июня 2018, 19:18
            0
            Попробуйте ваши накрученные значения вместо записи в ТВшки likes и dislikes, писать их в соседние, например likes_fake, dislikes_fake. В плагине конечно оставить всё, как есть, а при выводе суммировать likes + likes_fake и с дизами тоже самое.
              Ксения
              09 июня 2018, 19:28
              0
              Я с fenom как-то совсем ни как
              в снипете заменить
              'likes' => $_modx->resource['likes']
              на
              'likes' => $_modx->resource['likes_fake'] + $_modx->resource['likes']
              ?

              PS Значения не накрученные, попрошу, просто переношу данные из LikeDislike )

                Павел Гвоздь
                09 июня 2018, 19:36
                0
                'likes' => ($_modx->resource['likes_fake'] + $_modx->resource['likes']),
                Примерно…

                Значения не накрученные, попрошу, просто переношу данные из LikeDislike )
                Без разницы. Ради удобства повествования.
                  Ксения
                  09 июня 2018, 20:30
                  0
                  Спасибо, всё заработало как надо.
                  PS При использовании tv надо в настройках системы cache_resource выставить на Нет
Наумов Алексей
17 июля 2017, 09:40
0
Привет, классно!

Вопросы от меня:
1. пример выборки для pdoResources (с LEFT JOIN имею ввиду) можно?
2. Как реализована защита от повторного голосования гостями и авторизованными пользователями?
    Павел Гвоздь
    17 июля 2017, 10:13
    0
    1) Пример выборки чего?
    2) IP или номер сессии. Для авторизованных юзеров не вижу смысла объяснять…
      Наумов Алексей
      17 июля 2017, 10:18
      0
      Даже не знаю чего у нас pdoResources выбирает… может быть ресурсы? :)
      Например — отсортировать по популярности.
        Павел Гвоздь
        17 июля 2017, 10:22
        +1
        Для справки: pdoResources способен выбирать все, что угодно. xLike способен работать с любыми объектами.
        Записываете рейтинг в ТВ при голосовании на событии xLikeOnVote и сортируете. Ничего сложного.
          Наумов Алексей
          17 июля 2017, 10:58
          0
          Не люблю TV плодить, да и как по нему сортировать то, он строка. С приведением типов разве, что тоже не гуд на больших объемах. Вот и спросил.
            Павел Гвоздь
            17 июля 2017, 12:20
            0
            Ну можно в таком случае добавить еще одно поле в таблицу ресурсов и прописать его в map массив. В этом случае в плагине на событие xLikeOnVote писать данные рейтинга в него. Тогда можно будет и сортировать без приведения типа.
              Руслан Кундиус
              17 июля 2017, 15:04
              0
              Думаю Алексей имел ввиду что-то вроде этого:
              {'pdoResources' | snippet : [
                  'loadModels' => 'xLike',
                  'where' => [
                      'xlVote.class' => 'modResource'
                  ],
                  'leftJoin' => [
                      'xlVote' => [
                          "class" => "xlVote",
                          "on" => "modResource.id = xlVote.parent"
                      ]
                  ],
                  'select' => [
                      'modResource' => '*',
                      'xlVote' => [
                          'COUNT(xlVote.value) as total',
                          'SUM(xlVote.value = 1) as likes',
                          'SUM(xlVote.value = -1) as dislikes'
                      ]
                  ]
              ]}
              Можно даже рейтинг посчитать, но это зашквар какой-то:
              '(((SUM(xlVote.value = 1) + 1.9208) / (SUM(xlVote.value = 1) + SUM(xlVote.value = -1)) - 1.96 * sqrt((SUM(xlVote.value = 1) * SUM(xlVote.value = -1)) / (SUM(xlVote.value = 1) + SUM(xlVote.value = -1)) + 0.9604) / (SUM(xlVote.value = 1) + SUM(xlVote.value = -1))) / (1 + 3.8416 / (SUM(xlVote.value = 1) + SUM(xlVote.value = -1))) * 100) as rating'
                Павел Гвоздь
                17 июля 2017, 15:10
                +1
                А зачем делать все эти подсчёты на стороне MySQL и джоины, когда в описанном мной способе преимущество в плане быстродействия и ресурсопотребления на лицо?
                  Руслан Кундиус
                  17 июля 2017, 15:23
                  0
                  Согласен, вариант с расширением таблицы гораздо лучше.
kalisto
24 октября 2017, 10:44
0
Здравствуйте! Прошу помощи — не накапливаются лайки.
В шаблоне вызываю чанк
[[!xLike?
          &parent=[[*id]]
          &tpl=`tpl.xLikeMy`
        ]]
Не работают и на xLike.tpl
Меняла
&mode=`local`
и
&mode=`db`
Пример здесь
Все равно не накпливаются лайки. как исправить?
    Павел Гвоздь
    26 октября 2017, 08:58
    0
    1) Для начала корректно пропишите вызов сниппета, строго по формату MODX.
    2) Потом мне не ясно, что вы хотите накапливать и куда?
    3) Я показал конкретные примеры на демо сайте, как надо вызывать и что из этого получается. Если почитать внимательно, то поймете, что к чему и как с этим работать.
    Игорь
    10 января 2018, 14:10
    0
    Вы решили вопрос с накоплением лайков? Не могли бы, если не сложно, здесь выложите решение и причину? Спасибо заранее.
      kalisto
      10 января 2018, 17:46
      0
      Наверно до конца не решила, взяла другой плагин LikeDislike
        Игорь
        10 января 2018, 17:47
        0
        Понял. Спасибо за отклик.
kalisto
24 октября 2017, 11:00
0
Также ставлю в списке ресурсов вывод
<small>Число голосов - {$likes | number_format : 0 : '' : ' '}</small>
Верно ли? Или как правильно вывести число лайков?
Fenom pdotools_fenom_parser активировала
    Павел Гвоздь
    26 октября 2017, 08:59
    0
    4) Где вы вызываете {$likes}? В чанке компонента xLike, надеюсь? Тогда работать должно.
      Денис
      31 октября 2017, 15:04
      0
      Здравствуйте. Подскажите как отсортировать ресурсы по рейтингу? Сейчас сортирует так: Если на одном ресурсе 1 лайк (рейтинг 20,65) а на другом 1 лайк / 1 дизлайк (рейтинг 9,45), то выводится на первое место 2-й ресурс с рейтингом 9,45.
      Создал плагин: xLikeOnVote с кодом который вы приводите в описании
      Вывод делаю так:
      [[!pdoPage?
                                              &element=`getTickets`
                                              &parents=`[[*id]]`
                                              &tpl=`afisha-popular`
                                              &hideContainers=`1`
                                              &includeTVs=`image, rating`
                                              &limit=`4`
                                              &sortby=`{"rating":"DESC"}`
                                          ]]
      В чанке afisha-popular вызываю
      [[!xLike? &parent=`[[+id]]` &tpl=`rating`]]
      Подскажите, как вывести на первое место ресурс с большим рейтингом?
Игорь
10 января 2018, 16:03
0
Так как всё же фильтровать ресурсы по рейтингам? В разделе используется mFilter
Александр
27 января 2018, 17:43
0
Здравствуйте, как скачать? не находит xLike в админке на сайте
slavkovladymyr
27 января 2018, 22:48
0
Подскажите как вывести текущее место в рейтинге для отдельного обьекта голосования? Если подробнее, то на сайте есть голосование за учасниц конкурса (modUser) и нужно на странице учасницы указать на каком она сейчас месте в рейтинге. Дизлайки скрыты, можно только ставить лайк, что и считаеться как один голос.
Станислав
29 января 2018, 01:48
0
Здравствуйте, подскажите, как правильно вставить в шаблон. Конструкция
[[!xLike?
     &guest = false
      ]]
работает, но на гостей никак не влияет и гости так же могут ставить лайки. или вызов параметров снипета в данном случае не сработают?
Михаил
03 апреля 2018, 21:53
0
Не понятно как считаеся рейтинг какие то проценты. Процент чего он считает?
    Павел Гвоздь
    03 апреля 2018, 22:09
    0
    Внимательнее.
    особенностью компонента является правильный подсчет рейтинга на основе вычисления нижней границы доверительного интервала Вильсона для параметра Бернулли
    habrahabr.ru/company/darudar/blog/143188/
      Михаил
      03 апреля 2018, 23:18
      0
      Это вычисление понятно только Вильсону и Бернулли )))) Обычный пользователь не поймёт ни чего…
Александр
01 июня 2018, 15:19
0
Добрый день. Напишите пожалуйста как этот компонент привязать к сниппету TicketComments, чтоб можно было комментарии лайкать без авторизации. Желательно без шаблонизатора феном.
Илья
08 июня 2018, 18:27
0
Как сделать конструкцию такого плана, аналогичную как на пикабу:
<span class="xlike__count xlike__count_like [ js-xlike-number ]">{$likes}-{$dislikes}</span>
Что бы обновлялось не при перезагрузке страницы, а ajax
    Илья
    08 июня 2018, 18:41
    0
    только таким образом получилось показывать общую цифру рейтинга
    <span class="xlike__count xlike__count_like xlike__count_dislike [ js-xlike-number ]">{$likes-$dislikes}</span>
    Но обновляется только после перезагрузки страницы
Ксения
09 июня 2018, 13:26
0
Как правильно записать это
{'!xLike' | snippet : [
    'mode' => 'local',
    'likes' => $_modx->resource['likes'],
    'dislikes' => $_modx->resource['dislikes'],
    'rating' => $_modx->resource['rating'],
]}
не в fenom?
[[!xLike?
          &parent=[[*id]]
          &mode=`local`
          &likes=`tv.likes`
          &dislikes=`tv.dislikes`
          &rating=`tv.myLike`
          &tpl=`tpl.xLike`
        ]]
что-то не работает корректно
Sergey
12 июня 2018, 20:36
0
Павел, подскажите пжл
Как правильно прописать чтобы после нажатия на likes появлялось кол-во лайков и рейтинг?
    Павел Гвоздь
    13 июня 2018, 11:39
    0
    Как будет работать, так и прописывайте. Нет тут понятия «правильно».
      Sergey
      22 июля 2018, 16:53
      0
      Подскажите, как сделать чтобы при нажатии на like потом кнопка становилась не кликабельна?
        Павел Гвоздь
        22 июля 2018, 20:15
        0
        Правками в JS.
          Sergey
          22 июля 2018, 20:34
          0
          Мда… ответ убедительный… :) Спасибо
KSin
05 июля 2018, 23:39
1
0
Добрый день. Подскажите такой момент.
Есть товары. miniShop2
В карточке товара вызываю:
[[!xLike?
&parent=[[*id]]
&mode=`db`
&likes=`$_modx->resource['likes']`
&dislikes=`$_modx->resource['dislikes']`
&rating=`$_modx->resource['rating']`
&tpl=`tpl.xLike`
&class=`msProduct`
&list=`default`
]]

Создал дополнительные TV поля:
likes
dislikes
rating

Далее ставлю лайк товару. Но при редактировании товара поля likes, dislikes, rating по-прежнему пусты. Что я сделал не так? Или чего-то не сделал? Заранее спасибо.
    Павел Гвоздь
    06 июля 2018, 07:50
    0
    Плагин нужен.
      KSin
      06 июля 2018, 16:17
      0
      Павел, а можно чуть подробнее? В описании есть код плагина и в комментах есть что-то похожее. И поясните пожалуйста. Создаю плагин в папке xLine? Как назвать, где его прописать, чтобы работало? Если не затруднит. Спасибо.
        Павел Гвоздь
        07 июля 2018, 09:55
        0
        switch ($modx->event->name) {
            case "xLikeOnVote":
                if ($class == 'modResource' && $list == 'default') {
                    if ($resource = $modx->getObject($class, array('id' => $parent))) {
                        $resource->setTVValue('rating', $rating);
                        $resource->setTVValue('likes', $likes);
                        $resource->setTVValue('dislikes', $dislikes);
                    }
                }
                break;
        }
          KSin
          07 июля 2018, 12:53
          0
          Спасибо
Константин
28 июля 2018, 16:02
0
Добрый день! Как правильно вызвать xLike не в текущем Тикете, а в списке Тикетов, с возможностью голосовать?
В текущем тикете так:
{'!xLike' | snippet : [
'mode' => 'db',
 'tpl'=>'myXLikeTpl',
'class' => 'Ticket',
'likes' => $_modx->resource['likes']
]}
Данные лайков заносятся в TV likes с помощью плагина.

Как сниппет вызвать в списке?
    Константин
    28 июля 2018, 21:21
    1
    0
    Решил проблему. Так вызвал:
    {'!xLike' | snippet : [
     'mode' => 'db',
    'parent'=> $id,
    'tpl'=>'myXLikeTpl',
    'class' => 'Ticket',
    'likes' => $_modx->resource.id | resource : 'likes'
    ]}
      Павел Гвоздь
      29 июля 2018, 11:51
      0
      Это не проблема. Это незнание базовых моментов.

      Как временно убрать ограничение голосования с одного IP, чтобы можно было проголосовать за ресурс несколько раз?
      Никак.
        Константин
        29 июля 2018, 20:40
        +1
        Это не проблема. Это незнание базовых моментов.
        Я с Вами совершенно согласен. Мне стыдно, но я учусь.

        По-поводу IP, мне бы хотелось отключать эту проверку где-нибудь в настройках. Сейчас ситуация такая. Есть фотография, которой пытаются поставить оценку, к примеру, три пользователя (каждый со своего устройства), подключенные к одной точке wi-fi. И если кто-то из них первый поставил оценку, то максимум, что могут сделать другие, это отменить оценку, поставленную первым проголосовавшим пользователем, а не оставить свою.
          Павел Гвоздь
          29 июля 2018, 21:12
          +1
          Обновляйтесь.
          1.0.3-beta (30.07.2018)
          ==============
          - В сниппет добавлен параметр &ip, позволяющий отключить проверку по IP

          На демо сайт добавил возможность «потыкать». Второй пример.
          Укажите сниппету xLike
          'ip' => false,
Константин
29 июля 2018, 00:16
0
Как временно убрать ограничение голосования с одного IP, чтобы можно было проголосовать за ресурс несколько раз?
Sergey
29 июля 2018, 16:11
0
Павел, делаю с помощью Вашего компонента голосование, скажите, можно ли убрать логику правильного подсчета рейтинга на основе вычисления нижней границы доверительного интервала Вильсона для параметра Бернулли?
Т.е. чтобы было как нормальное голосование без учета дизлайка? Куда копать вообще?
    Павел Гвоздь
    29 июля 2018, 16:15
    0
    Убрать кнопку дизлайка не получается?
      Sergey
      29 июля 2018, 16:22
      0
      Я убрал, но мне тут заявляют, что голосование работает не так как надо и что типа у кого больше голосов там должно быть 100% на шкале…
      Сейчас к примеру 4 голоса и 51%, при скольки голосах будет 100%?
        Павел Гвоздь
        29 июля 2018, 16:27
        0
        А как надо? И кто вам это заявляет?..
        Павел Гвоздь
        29 июля 2018, 16:32
        0
        Тот кто вам это заявляет не понимает принципа работы компонента. Шкала рейтинга не на основании всего кол-ва записей, за которые голосовали (хотел бы я посмотреть, как долго будет отрабатывать такой скрипт, учитывая, что записи постоянно пополняются, а кол-во голосов увеличивается), а на основании формулы, упомянутой вами выше, относительно кол-ва лайков и дизлайков и ещё некоторых моментов, которые используются в формуле.
        Проще говоря, если вам надо отсортировать записи на основании только лайков, то сортируйте по лайкам, а не по рейтингу. А если вам надо вывести процентный рейтинг текущей записи, учитывая все записи, то пишите эту логику самостоятельно.
          Sergey
          29 июля 2018, 16:36
          0
          Спасибо.
          Sergey
          31 июля 2018, 12:37
          0
          Вот логика
          www.strawpoll.me/107782

          Т.е. например, проголосовало 10 человек — это 100%
          1 работа — 5 человек — 50%
          2 работа — 3 человека — 30%
          3 работа — 2 человека — 20%
          Всего — 10 человек — 100 %

          Вашим компонентом такого не сделаешь?
Алексей
26 ноября 2018, 21:41
0
Здравствуйте! Немного странный вопрос, есть большое желание настроить на сервере fastcgi_cache (то есть фактически кешируется весь html код страницы, если произошли изменения он обновляется.)
Проблема в том, что если один юзер проголосовал, на странице подставится класс link_active и она закешируется. Зайдет другой пользователь и ему отдастся страница уже с классом link_active (если он нажмет кнопку с этим классом, по идее ему отдастся окно access denied, но не уверен) Есть возможность настраивать исключения для кеширования, но что тут исключать не понятно. Если кто сталкивался с подобным, поделитесь опытом.

Upd хотя вроде всё работает…
Периодически откуда-то всплывает класс link_active там где его не должно быть, но после обновления страницы уходит
    Павел Гвоздь
    27 ноября 2018, 07:24
    0
    Класс .xlike__link_active ставится на тот элемент, который был выбран пользователем. По fastcgi_cache вряд ли подскажу.