Pull to refresh

Пишем модуль рейтинга для Joomla, или AJAX по-русски

Reading time7 min
Views7.6K
imageПри работе над сайтом у меня возникла необходимость в системе оценки статей, при этом она должна была быть легкой и простой. От пятибальной было решено отказаться сразу, и была выбрана система «Нравится/Не нравится». К сожалению быстрый поиск ничего подобного не нашел, и поэтому было решено писать модуль с нуля.

С чего начать?

Начало модуль берет из учебного примера Hello world! Найти его не составит труда, качаем и распаковываем в папку. Так как модуль будет работать с базой данных, а стандартная таблица рейтинга нам не подходит, то создадим собственную.
image
  • 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 базы данных заполняется и учитывается, но вот значение из неё никуда не идет, да, это так, но никто не мешает вам её использовать.

Результат

image
Вот что в конечном итоге вышло у меня, функциональность полностью аналогична подобным кнопкам на YouTube, каюсь, временное оформление тоже оттуда. Если кому то потребуется, могу выслать архив с файлами, сейчас времени мало, стучать в Skype

У меня ничего не работает, что делать?

Ниже приведен список полезных ссылок по данной теме:
Временно.НЕТ
Class: Request
My SQL String Functions
Basic AJAX Requests Using MooTools 1.2

От автора

Это мой первый топик на хабре, и надеюсь не последний. Возможно тут сильно подробно всё расписано, однако подробных описаний довольно мало и возникают трудности с которыми сталкивался в свое время и я, думаю эта статья поможет новичку сэкономить пару часов жизни.
Tags:
Hubs:
Total votes 15: ↑7 and ↓8-1
Comments12

Articles