Drupal 7. Модуль подписок своими руками

Вместо предисловия

Функциональность подписок на какой-либо контент – одна из востребованных в веб-индустрии. Многие сайты могут похвастаться подобным. И наш проект не стал исключением. Дано: сайт на Drupal 7. Что требуется: найти или написать модуль, реализующий все необходимые функции. Какой вариант был выбран и что из этого вышло, Вы можете прочитать далее.

Поиск подходящих модулей

Самый очевидный и угадываемый вариант – модуль Subscriptions. Данный модуль позволяет пользователям подписываться на изменения нод и таксономии (в том числе, новые комментарии к контенту). Главной причиной отказа от этого варианта послужило отсутствие возможности подписки без перезагрузки страницы и ориентированность модуля в основном на email-рассылки. Другой вариант – модуль Notifications. В принципе, его расширяемость и масштабируемость нам подходили (для создания своих типов подписок), но отсутствие стабильной версии под 7.x не позволило нам его использовать. Итак, решение оставалось только одно – написать модуль самим.

Первоначальная настройка

Полностью описывать весь модуль не имеет смысла (это точно выйдет за пределы темы статьи), поэтому опишу основной функционал и начну с основных моментов: настройки админки. Во-первых, необходимо создать тип контента (в моём примере – это Item) с некоторым количеством полей. Здесь и далее основное внимание будет уделяться полям Title, Body, Image.

Во-вторых, следующий этап – создание Views для вывода нод данного типа. К примеру, как на следующем рисунке: Помимо описанных ранее полей в данной Views выводится также Nid. Для чего же это требуется? Темизировав это поле (к которому впоследствии будет привязано событие подписки), мы можем настроить его вывод по своему усмотрению.

Файлы модуля и их описание

Чтобы начать разработку модуля, создадим в sites/all/modules папку custom_subscriptions. Нам понадобятся 4 файла: custom_subscriptions.info (основная информация о модуле), custom_subscriptions.install (настройки инсталляции), custom_subscriptions.module (собственно, сам файл модуля) и custom_subscriptions.js (в скрипте будет реализован механизм подписки без перезагрузки страницы). Теперь о каждом из них подробнее.

Инфо-файл

Содержимое файла custom_subscriptions.info:

name = "Custom Subscriptions"
description = "Allows users to follow content"
core = 7.x
version = 7.x-1.0
php = 5.1
scripts[] = custom_subscriptions.js

Как видим, файл скрипта был подключён именно здесь.

Файл инсталляции

Для примера, размещённого здесь, будет достаточно одной таблицы в базе данных, содержащей поля uid (идентификатор пользователя) и nid (идентификатор ноды, на которую пользователь подписан). Содержимое файла custom_subscriptions.install:

/**
 * Implements hook_schema() 
 */
function custom_subscriptions_schema() {
  $schema['custom_subscriptions'] = array(
    'description' => t('The base table for subscriptions'),
    'fields' => array(  
      'sid' => array(  
        'type' => 'serial',  
        'unsigned' => TRUE,  
        'not null' => TRUE,
      ),       
      'uid' => array(  
        'type' => 'int',  
        'length' => 10,        
        'not null' => TRUE,
      ),  
      'nid' => array(  
        'type' => 'int',  
        'length' => 10,        
        'not null' => TRUE,
      ),   
    ),  
   'primary key' => array('sid'), 
  ); 
  return $schema;
}

После инсталляции таблица будет иметь следующую структуру:

Настройка темы

Прежде всего, следует создать шаблон для вывода Nid во Views. В нашем случае он имеет имя views-view-field--items--page--nid.tpl.php (имя для шаблона можно получить в разделе Advanced / Theme в настройках Views). Файл шаблона размещается в теме (была использована подтема Zen под названием STARTERKIT), в папке templates. Содержимое файла views-view-field--items--page--nid.tpl.php:

global $user;
if ($user->uid != 0): 
  $following = db_select('custom_subscriptions', 'cs')
    ->fields('cs')
    ->condition('uid', $user->uid)
    ->condition('nid', $output)
    ->execute()
    ->rowCount();
  if ($following == 0):
    print('');
  else:
    print('FOLLOWING'); 
  endif;
endif;

Как видно из представленного кода, поле подписок доступно только для авторизированных пользователям. Кроме того, в зависимости от наличия записи в таблице custom_subscriptions, заголовок поля меняется с FOLLOW (если пользователь ещё не подписан на ноду) на FOLLOWING (если подписка уже имеется). Информация о совершаемом действии (follow/unfollow) и идентификатор ноды nid будут храниться в атрибуте href.

Разработка механизма подписки

Так как функциональность файла модуля и скрипта взаимосвязаны, они будут рассмотрены в совокупности. Начнём с визуальных эффектов: в файле скрипта добавим появление поля подписок по наведению на контент, а также изменение текста надписи с FOLLOWING на UNFOLLOW при наведении на это поле (так же, как это осуществлено в Twitter). Содержимое файла custom_subscriptions.js:

(function ($) {
  Drupal.behaviors.collectiveMove = {
    attach: function(context, settings) {
      // show buttons on hover
      $('.view-items .views-row').hover(function() {
        $(this).find('.views-field-nid a').css('display', 'block');
      });

      // hide buttons on mouseleave
      $('.view-items .views-row').mouseleave(function() {
        $(this).find('.views-field-nid a.follow, .views-field-nid a.unfollow').css('display', 'none');
      });

      // show 'unfollow' when user has already had a subscription
      $('.view-items .views-row .views-field-nid .following').live('mouseover', function() {
        $(this).text('UNFOLLOW');
        $(this).addClass('unfollow');
      });

      // hide 'unfollow' when user has already had a subscription
      $('.view-items .views-row .views-field-nid .following').live('mouseout', function() {
        $(this).text('FOLLOWING');
        $(this).removeClass('unfollow');
      });
    }
  };

})(jQuery);

Теперь начинается самое интересное – передача данных между файлом модуля и скриптом. Основная идея в следующем: при нажатии на FOLLOW post-запрос передаёт данные о действии ('follow') и идентификаторе ноды в модуль, где полученные данные записываются в базу. Для UNFOLLOW механизм сходен, но действие будет другим ('unfollow'), и полученные данные будут удаляться из таблицы базы данных. Для начала создадим страницу, на которую будет отправляться запрос, используя hook_menu. Содержимое файла custom_subscriptions.module:

/*
 Implements hook_menu()
*/
function custom_subscriptions_menu() { 
  $items = array();   
  $items['custom_subscriptions_ajax'] = array(
    'title' => 'Ajax callback', 
    'page callback' => 'custom_subscriptions_ajax',
    'access callback' => TRUE, 
    'type' => MENU_CALLBACK, 
  ); 
  return $items;
}

Напоминаю, что по правилам оформления кода модулей для Drupal, ставится только открывающий тег php. Страница готова – добавим функционал. Содержимое файла custom_subscriptions.module:

/*
 Implements hook_menu()
*/
function custom_subscriptions_menu() { 
  ...
}

/**
 * Implements ajax callback function
 */
function custom_subscriptions_ajax() {
  global $user;   
  if($_POST['type'] == 'follow') {
    if ($_POST['nid']) {      
      db_insert('custom_subscriptions')
        ->fields(array(
          'uid' => $user->uid,
          'nid' => $_POST['nid'],
        ))
        ->execute();
      print(1); 
    } 
  }
  else if($_POST['type'] == 'unfollow') {
    if ($_POST['nid']) {
      db_delete('custom_subscriptions')  
        ->condition('uid', $user->uid)
        ->condition('nid', $_POST['nid'])
        ->execute();      
      print(1); 
    } 
  }
  else {
    print(0);
  }
}

В зависимости от полученного из post-запроса действия (follow/unfollow), добавляем либо удаляем запись из таблицы. Каждая запись идентифицируется по uid (берём идентификатор глобального юзера) и nid. В случае успеха печатаем '1'. Теперь осталось самое важное – передать данные по нажатию на поле nid на странице, сформированной с помощью Views. Содержимое файла custom_subscriptions.js:

(function ($) {

  Drupal.behaviors.collectiveMove = {
    attach: function(context, settings) {
      ...

      // click on follow/unfollow buttons
      $('.view-items .views-row .views-field-nid a').bind('click', function(e) {
        e.preventDefault();        
        var href = $(this).attr('href');        
        var strs = href.split('/');
        var item = $(this);
        $.post(
          Drupal.settings.basePath + 'custom_subscriptions_ajax',
          {
            type: strs[0],
            nid: strs[1],             
          },
          function (data) {
            if (data == 1) {
              if (strs[0] == 'follow') {
                item.text('UNFOLLOW');
                item.removeClass('follow');
                item.addClass('following unfollow');
                item.attr('href', 'unfollow/' + strs[1]);
              }   
              if (strs[0] == 'unfollow') {
                item.text('FOLLOW');
                item.removeClass('following');
                item.removeClass('unfollow');
                item.addClass('follow');
                item.attr('href', 'follow/' + strs[1]);
              }          
            }
          }
        );         
      });
    }
  };

})(jQuery);

При нажатии на ссылку в поле nid мы проходим следующие шаги:

  • запрет выполнения действия по умолчанию (чтобы предотвратить перезагрузку страницы);
  • получение действия и nid из атрибута href, в который они были записаны в шаблоне;
  • передача полученных данных на подготовленную в модуле страницу;
  • после получения успешного результата меняем название и некоторых атрибуты выводимой ссылки.

Вместо послесловия

В данной статье был описан процесс создания простейшего модуля подписок на ноды в Drupal 7. Основным моментом можно назвать осуществление подписок без перезагрузки страницы, используя post-запрос. На готовый модуль можно взглянуть здесь: https://github.com/Sacret/custom_subscriptions

 

Последние твиты

Контакты