При работе над сайтом у меня возникла необходимость в системе оценки статей, при этом она должна была быть легкой и простой. От пятибальной было решено отказаться сразу, и была выбрана система «Нравится/Не нравится». К сожалению быстрый поиск ничего подобного не нашел, и поэтому было решено писать модуль с нуля.С чего начать?
Начало модуль берет из учебного примера Hello world! Найти его не составит труда, качаем и распаковываем в папку. Так как модуль будет работать с базой данных, а стандартная таблица рейтинга нам не подходит, то создадим собственную.

- id — главное поле с автоинкрементом
- nid — уникальное поле хранящее id материала
- like — список id пользователей кому понравилась новость
- dislike — список id пользователей кому не понравилась новость
- rate — общий рейтинг новости
Итак с базой данных разобрались, теперь приступим к самому модулю. Педполагаю что вы уже знаете что такое AJAX, для чего он нужен. Так как в Joomla не предусмотрена возможность использования AJAX модулями то мы восмользуемся фичей которую предлагает Тушов Леонид, а именно создадим файл через который и будут идти все запросы. В результате у нас теперь 2 файла в папке модуля: ajax.php и mod_likerating (он же Hello World!). Теперь пишем для них xml файл инсталлятор и устанавливаем.
<?xml version="1.0" encoding="utf-8"?> <install type="module" version="1.5.0"> <name>JLikeRating</name> <author>StyleT</author> <version>1.0</version> <description>Модуль голосования для Joomla созданный по принципу нравится/не нравится</description> <files> <filename module="mod_likerating">mod_likerating.php</filename> <filename module="mod_likerating">ajax.php</filename> </files> <params> <param name="suffix" type="text" default="" label="Суффикс класса модуля" description="Позволяет присвоить суффикс классу модуля" /> </params> </install>
Пишем код модуля
Проверяем вывод модуля и идем дальше. Так как модуль будет выводится в полной версии новости, то выводить мы его будем must have плагином Modules Anywhere с обязательно включенными параметрами Enable parameter overriding, Ignore module state и Enable in components.
К сожалению модуль не знает id открытого в данный момент материала, поэтому мы его передадим в виде параметра используя функцию Parameter overriding плагина Modules Anywhere:
{module 65|newsid=<?php echo $this->article->id; ?>} 65 это id нашего модуля полученный после установки.
Итого код модуля у нас стал вот таким:
<?php defined('_JEXEC') or die('Restricted access'); $db = JFactory::getDBO();//Подключили БД $user = JFactory::getUser();//Данные пользователя $suffix = $params->get("suffix");//Суффикс класса модуля $newsid = $params->get("newsid");//Получили id новости if (JRequest::getVar('voteresult')) { //Работа с AJAX echo 'hello from AJAX\n'.JRequest::getVar('voteresult'); } else{ //Вывод кода модуля ?> <script type="text/javascript"> function myAJAXSendRequest(voteres) { new Request({ method: 'get', url: '/modules/mod_likerating/ajax.php', data: { 'voteresult' : voteres}, onSuccess: function(responseText) { alert(responseText);}, onFailure: function() { alert('failed');} }).send(); } window.addEvent('domready', function() {//Вешаем события на кнопки $('likerating-like').addEvent('click', function(e) {myAJAXSendRequest('like');}); $('likerating-dislike').addEvent('click', function(e) {myAJAXSendRequest('dislike');}); }); </script> <DIV class="likerating<?php echo $suffix; ?>"> <button class="like" type="button" id="likerating-like" > <span class="label">Нравится</span> </button><button class="dislike" type="button" id="likerating-dislike"> <span class="label">Не нравится</span> </button> </div> <?php } ?>
Все получилось? Работает? Отлично, теперь разберем этот код по шагам:
1. Плагин Modules Anywhere вызывает модуль и передает ему id новости.
2. Модуль подключает классы и получает параметры.
3. Код «if (JRequest::getVar('voteresult'))» дает false так как ajax запроса не было.
4. Вывод модуля пользователю.
5. Пользователь жмет кнопку Нравится
6. Параметры указанные в data класса Request передаются на указанный url
7. Вызванный ajax.php вызывает модуль
8. Код «if (JRequest::getVar('voteresult'))» и выполнется только часть обработки ajax, сервер отвечает
9. Вызывается onSuccess и обрабатывается ответ сервера.
10. Все это без перезагрузки страницы
Важно отметить что сейчас мы передаем данные в текстовом формате, тогда как new Request.JSON уже работает с форматом JSON что предпочтительнее если принимаем несколько переменных.
Пишем собственно голосование
Итак ниже я привожу полный код модуля голосования для которого все и затевалось.
<?php defined('_JEXEC') or die('Restricted access'); $db = JFactory::getDBO();//Подключили БД $user = JFactory::getUser();//Данные пользователя $suffix = $params->get("suffix");//Суффикс класса модуля $newsid = $params->get("newsid");//Получили id новости if (JRequest::getVar('voteresult')) { //Если true работаем с ajax $voteres= JRequest::getVar('voteresult');//Как пользователь проголосовал $voted= JRequest::getVar('voted');//Голосовал ли он ранее, и если да, то как 0-нет 1-нравилось 2-не нравилось $newsid= JRequest::getVar('newsid');//Получили id новости так как плагин Modules Anywhere нас уже не вызывал $query="UPDATE `#__likerating_news` SET "; if($voteres=='like') { switch($voted) { case 1: $query.="`like`=(SELECT REPLACE(`like`,',".$user->id."','')), `rate`=`rate`-'1'"; $answer= 0; break; case 2: $query.="`like`=(SELECT CONCAT_WS(',', `like`, '".$user->id."')), `rate`=`rate`+'2', `dislike`=(SELECT REPLACE(`dislike`,',".$user->id."',''))"; $answer= 1; break; case 0: $query.="`like`=(SELECT CONCAT_WS(',', `like`, '".$user->id."')), `rate`=`rate`+'1'"; $answer= 1; break; } }else if($voteres=='dislike') { switch($voted) { case 1: $query.="`dislike`=(SELECT CONCAT_WS(',', `dislike`, '".$user->id."')), `rate`=`rate`-'2', `like`=(SELECT REPLACE(`like`,',".$user->id."',''))"; $answer= 2; break; case 2: $query.="`dislike`=(SELECT REPLACE(`dislike`,',".$user->id."','')), `rate`=`rate`+'1'"; $answer= 0; break; case 0: $query.="`dislike`=(SELECT CONCAT_WS(',', `dislike`, '".$user->id."')), `rate`=`rate`-'1'"; $answer= 2; break; } } $query.=" WHERE `nid`='".$newsid."'"; $db->setQuery($query); $result = $db->loadResult(); echo $answer; } else{//Если AJAX запроса не было //Создаем строку в таблице для новости если она ещё не была создана $db->setQuery("INSERT INTO #__likerating_news (`nid`, `like`, `dislike`, `rate`) VALUES ('".$newsid."', '0', '0', '0')"); $result = $db->loadResult(); //Голосовал уже или нет? Тут можно сделать более рационально $db->setQuery("SELECT `like`, `dislike` FROM `#__likerating_news` WHERE `nid`='".$newsid."'"); $result = $db->loadRow(); if(strpos("t".$result[0], $user->id)) $voted= '1'; else if(strpos("t".$result[1], $user->id)) $voted= '2'; else $voted= '0'; ?> <script type="text/javascript"> var voted= '<?php echo $voted; ?>';//Чтобы не пересчитывать храним переменные в JS var newsid= '<?php echo $newsid; ?>';//Плагин будет вызываться из ajax.php и нам нужно вручную передать параметр function myAJAXSendRequest(voteres) { new Request({ method: 'get', url: '/modules/mod_likerating/ajax.php', data: { 'voteresult' : voteres , 'voted' : voted, 'newsid' : newsid}, onSuccess: function(responseText) { voted= responseText; if(voted==0){//Смена картинки в кнопке $('likeratingimg-like').setStyle('background-position', '0px -38px'); $('likeratingimg-dislike').setStyle('background-position', '0px 0px'); }else if(voted==1){ $('likeratingimg-like').setStyle('background-position', '0px -58px'); $('likeratingimg-dislike').setStyle('background-position', '0px 0px'); }else{ $('likeratingimg-like').setStyle('background-position', '0px -38px'); $('likeratingimg-dislike').setStyle('background-position', '0px -19px'); } //alert(voted); }, onFailure: function() {//При неудаче выводим сообщение alert('failed'); } }).send(); } window.addEvent('domready', function() {//Вешаем события на кнопки $('likerating-like').addEvent('click', function(e) {myAJAXSendRequest('like');}); $('likerating-dislike').addEvent('click', function(e) {myAJAXSendRequest('dislike');}); }); </script> <DIV class="likerating<?php echo $suffix; ?>"> <button class="like" type="button" id="likerating-like" > <img id="likeratingimg-like" class="hand-image" style="<?php if($voted== '1'){echo 'background-position: 0px -58px;';}?>" src="/modules/mod_likerating/images/pixel.gif"> <span class="label">Нравится</span> </button><button class="dislike" type="button" id="likerating-dislike"> <img id="likeratingimg-dislike" class="hand-image" style="<?php if($voted== '2'){echo 'background-position: 0px -19px;';}?>" src="/modules/mod_likerating/images/pixel.gif"> </button> </div> <?php } ?>
Предполагается что background картинки состоит из спрайта который указан в CSS стиле, а вот написать собственно CSS и будет вашим заданием.
Важно и то что ajax.php трогать не надо вообще, просто ложим его в папку модуля и кидаем на него ajax запросы.
Все наверно заметили что ячейка rated базы данных заполняется и учитывается, но вот значение из неё никуда не идет, да, это так, но никто не мешает вам её использовать.
Результат

Вот что в конечном итоге вышло у меня, функциональность полностью аналогична подобным кнопкам на YouTube, каюсь, временное оформление тоже оттуда. Если кому то потребуется, могу выслать архив с файлами, сейчас времени мало, стучать в Skype
У меня ничего не работает, что делать?
Ниже приведен список полезных ссылок по данной теме:
Временно.НЕТ
Class: Request
My SQL String Functions
Basic AJAX Requests Using MooTools 1.2
От автора
Это мой первый топик на хабре, и надеюсь не последний. Возможно тут сильно подробно всё расписано, однако подробных описаний довольно мало и возникают трудности с которыми сталкивался в свое время и я, думаю эта статья поможет новичку сэкономить пару часов жизни.