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:

<?php

/**
 * 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:

<?php global $user; ?>
<?php if ($user->uid != 0): ?>
  <?php $following = db_select('custom_subscriptions', 'cs')
    ->fields('cs')
    ->condition('uid', $user->uid)
    ->condition('nid', $output)
    ->execute()
    ->rowCount();
  ?>
  <?php if ($following == 0):?>
    <?php print('<a href="follow/' . $output . '" class="follow">FOLLOW</a>'); ?>
  <?php else:?>
    <?php print('<a href="unfollow/' . $output . '" class="following">FOLLOWING</a>'); ?>
  <?php endif; ?>
<?php 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:

<?php

/*
 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:

<?php

/*
 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-запрос.
На готовый модуль можно взглянуть здесь: github.com/Sacret/custom_subscriptions

Similar posts

Ads
AdBlock has stolen the banner, but banners are not teeth — they will be back

More

Comments 9

    0
    Глядя на код хочется плакать.
      0
      Я поясню этот коммент, дабы было понятно для новичков.
      Про модуль Flag и .info файл уже сказали ниже, разберём другие моменты.
      1. Шаблоны предназначены для формирования конечного HTML. Логики в них быть не должно. В вашем случае необходимо написать handler для views, который бы формировал (на основе прав доступа) нужные голоса и ссылки.
      2. Проверка на зарегенного пользователя: if ($user->uid != 0) это негибко, используйте hook_permission и проверяйте права через user_access, это позволит управлять функционалом на уровне прав пользователя, что гораздо удобнее.
      3. Изучите Ajax framework, который позволит не городит велосипедов из JavaScript. В вашем случае можно было обойтись вообще без JS с таким же объёмом php-кода.

      Посмотрите модуль examples — там собрано множество примеров, которые могут пригодится в жизни )
      +1
      Можно было использовать Flag ( drupal.org/project/flag ), у него есть поддержка AJAX, интеграция с Views и Rules.
        0
        Спасибо за ссылку. Раньше с этим модулем не сталкивалась, надо будет изучить подробнее.
        0
        1. views-view-field--items--page--nid.tpl.php — в шаблонах нельзя делать запросы в БД, в Друпал 8 специально будет другой шаблонизатор, что бы такого не было
        2. version = 7.x-1.0 эту строку писать не нужно в кастомном модуле и версию РНР не нужно указывать если у вас нет явных расположенностей именно к версии
        3. Аякс функция возвращает не print(1)
        4. Проверьте код модулем Coder

        Поздравляю с начинанием программировать в Друпал!
          0
          Отличный коммент, и критика по делу. Спасибо Вам большое, всё учту на будущее.
          0
          Если в js используете attach, то используйте уже и context + once, чтобы по нескольку раз не привязываться к одним и тем же элементам.
            0
            Подписать человека какие-либо события не проблема, а вот как вы уведомляете подписчиков о событии? Может не по глазам но в коде не увидел.
              0
              Это как раз и было бы продолжение статьи (мне не хотелось нагромождать всё в одну)

            Only users with full accounts can post comments. Log in, please.