Ticket-рейтинг пользователей

Добрый день уважаемые дамы и господа. Несколько постов назад поднималась тема Ticket рейтингов, где ребята решили проспонсировать добавление в Tickets такого функционала как сортировка пользователей по количеству опубликованных постов, по количеству написанных комментариев и по количеству просмотров.

Некоторое время назад я тоже интересовался присвоению каждому пользователю рейтинга и очень удивился когда обнаружил что на modx.pro этого нет, несмотря на то, что есть возможность ставить лайки и дизлайки к тикетам и комментариям. Погуглив я нашел причину отсутствия такого рейтинга. Василий отписывал что появится куча недовольных дизлайками и отрицательными рейтингами, будут выяснения кто кому и за что поставил минус и.т.п. Его можно понять, ведь недовольные будут обращаться за разъяснениями и к нему. Я же решил попробовать реализовать подобный функционал на одном тестовом сайте.

Идея заключалась в следующем:

Рейтинг пользователя = (сумма рейтингов тикетов пользователя) + (сумма ретингов комментариев пользователя)/10

Я посчитал, что лайк к посту должен цениться больше чем лайк к комментарию, поэтому лайк к комментарию оценивается как +0.1 к рейтингу пользователя, аналогично с дизлайком.

Так как с разработкой под modx у меня всё очень плохо и рейтинги я не планировал применять на боевых сайтах, принял решение реализовать по принципу «лишь бы работало». Посмотрев структуру базы данных мы обнаруживаем что каждый лайк и дизлайк к тикету и комментарию это соответствющая запись в таблице «tickets_votes» в которой нас интересуют следующие поля:

owner — id пользователя, которому ставится лайк/дизлайк.
class — (TicketComment/Ticket) к чему относится голос — комментарий или тикет.
value — (0,1,-1) нейтральный голос, лайк, дизлайк.

Нам остаётся имея id пользователя вытащить сумму его рейтингов к Тикетам и комметариям, а затем обработать его в зависимости от коэффициента. Не могу сказать, насколько правильно я это сделал, но результат вылился в следующее:

[[!pdoPage?
    &element=`pdoUsers`
    &groups=`Users`
    &tpl=`tpl.User.info`
    &showLog=`1`
        &leftJoin=`{"TicketVote":{
                        "class": "TicketVote", 
                        "on": "modUser.id = TicketVote.owner AND TicketVote.class = 'Ticket'"
                    },
                    "TicketVoteComment":{
                        "class": "TicketVote", 
                        "on": "modUser.id = TicketVoteComment.owner AND TicketVoteComment.class = 'TicketComment'"
                    }
                }`
    &select=`{
                "modUser": "*",
                "TicketVote":"IFNULL(SUM(TicketVote.value),0) as TicketVote",
                "TicketVoteComment":"IFNULL(SUM(TicketVoteComment.value),0) as TicketVoteComment"
             }`
    &groupby=`modUser.id`
    &sortby=`{"TicketVote":"desc",
              "TicketVoteComment":"desc"
              }`
    &sortdir=`desc`
    &prepareSnippet=`prepareUserRating`
]]

[[!+page.nav]]

Здесь мы выводим список пользователей из группы «Users», сортируя их по нашему рейтингу. Прежде чем вывести результат мы его подготавливаем при помощи сниппета «prepareUserRating». Ведь нам нужно сложить рейтинг к тикетам и рейтинг к комментариям не забыв про коэффициент:

$userRating = $row['TicketVote'] + $row['TicketVoteComment']/10;

if ($userRating>0) { $userRating = '+'.$userRating;  $raitingClass = 'UserRatingPositive'; }
elseif ($userRating<0) $raitingClass = 'UserRatingNegative'; 
else { $userRating='0'; $raitingClass = 'UserRatingNeutral'; }

$row['TicketVote'] = '<span class="'.$raitingClass.'">'.$userRating.'</span>';

return serialize($row);
В зависимости от положительного, отрицательного или нейтрального конечного рейтинга мы его оборачиваем в соответствующий класс. В дальнейшем через CSS сможем положительный рейтинг окрашивать зелёным цветом, а отрицательный — красным. Получаем что-то вроде этого:


Вывод рейтинга конкретного пользователя



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

[[!pdoUsers? 
        &users=`[[!getUserId]]` 
        &tpl=`tpl.User.info`
                &leftJoin=`{"TicketVote":{
                        "class": "TicketVote", 
                        "on": "modUser.id = TicketVote.owner AND TicketVote.class = 'Ticket'"
                    },
                    "TicketVoteComment":{
                        "class": "TicketVote", 
                        "on": "modUser.id = TicketVoteComment.owner AND TicketVoteComment.class = 'TicketComment'"
                    }
                }`
                &select=`{
                            "modUser": "*",
                            "TicketVote":"IFNULL(SUM(TicketVote.value),0) as TicketVote",
                            "TicketVoteComment":"IFNULL(SUM(TicketVoteComment.value),0) as TicketVoteComment"
                         }`
                &groupby=`modUser.id`
                &sortby=`{"TicketVote":"desc",
                          "TicketVoteComment":"desc"
                          }`
                &sortdir=`desc`
                &prepareSnippet=`prepareUserRating`
    ]]

Здесь мы вызываем тот же сниппет pdoUsers, но выводим не всех пользователей, а конкретного — по id (&users=[[!getUserId]]). getUserId — это мой самописный небольшой сниппет, который возвращает id нужного пользователя, его текст приводить не буду, у каждого свои нужды и реализация, принцип должен быть понятен.

Заключение



Данный способ нельзя назвать корректным. Каждый раз при выводе рейтинга нам придётся его подсчитывать заново, выполняя leftJoin. Пока таблица «tickets_votes» имеет мало записей это может работать нормально, но со временем «tickets_votes» может разрастись до десятков и сотен тысяч записей и каждый раз производить выборку среди них не очень разумно. Поэтому, прежде чем исползовать эти примеры на боевых сайтах, подумайте)

В идеале, для реализации подобных рейтингов необходимо заводить доп. таблицу со связью User-id — TicketRating, где TicketRating у соответствующего пользователя должен обновляться сразу же при лайке/дизлайке. Немного поковыряв js файлы Tickets я нашёл места куда можно впихнуть нужные SQL запросы, но это всё же костыли, которые будут слетать при обновлении компонента, поэтому реализовывать такой вариант я не стал.

Всем добра!
An Si
14 апреля 2015, 09:17
modx.pro
6
2 717
+5

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

Василий Наумкин
14 апреля 2015, 13:07
0
Вот именно, что при джоинах всё это адово тормозит.

Настолько, что сейчас даже количество комментариев тикета подсчитывается отдельным запросом, после основного. А считать лайки, дизлайки и просмотры на паре тысяч юзеров, а потом сортировать по ним — это проще самому застрелиться.

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

Потому и запустили эту тему. Кстати сказать, рейтинги-то мы по-любому сделаем, а вот на уборку кода собрали пока только половину.
    Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
    1