Пример разработки модуля для Drupal 5.1 с использованием xajax.

Главные вкладки

Аватар пользователя Ромка Ромка 23 мая 2007 в 12:15

В данной статье приводится пример разработки собственного модуля для Drupal 5.1 с использованием библиотеки xajax версии 0.2.4.

Задача.
1. Реализовать возможность формирования пользователем в своем профиле "списка интересов" на основе одного из словарей, созданном в модуле Taxonomy.
2. Разработать систему поиска пользователей со схожими интересами.
3. Использовать в модуле технологию AJAX.

Работающий пример решения задачи можно найти здесь.

Реализация.
1. Формировать списки интересов позволяет модуль User tags.
2. Для поиска по интересам будет использоваться модуль, описанный в данной статье. Для наведения красоты будет использована библиотека xajax версии 0.2.4.

0. Предварительные действия.
Перед началом работы скачайте библиотеку xajax версии 0.2.4. На момент написания статьи эта версия xajax является последним релизом, возможно, под более новые версии этой библиотеки придется модифицировать код модуля.

Папку /xajax_js из архива скопируйте в корень Друпала (то есть в папку, в которой находится index.php). Файлы xajax.inc.php, xajaxCompress.php и xajaxResponse.inc.php скопируйте в папку /includes.

Также, скачайте файл xajax_loading.js_.txt, прикрепленный к этой статье, переименуйте его в xajax_loading.js и скопируйте в папку /xajax_js.

Пара слов о словаре со списком интересов. В меню "Categories" ("Словари" – admin/content/taxonomy) создайте новый словарь и необходимое количество терминов в нем. Словарь, для этого конкретного примера, должен быть двухуровневым. То есть, в его настройках должен быть отмечен пункт "Hierarchy – Multiple" ("Структура – Множественная"). Термины первого уровня станут категориями, которые нельзя будет выбрать, термины второго уровня станут параметрами, доступными для выбора пользователем.

1. Создание списка интересов.
Скачайте и установите модуль User tags. Далее в его настройках (admin/settings/user_tags) выберите словарь, на основе которого будет формироваться список, а в контроле доступа (admin/user/access) установите галочки "tag users" для всех ролей кроме "anonymous user".

После выполнения этого этапа в профиле пользователя появится вкладка "Tags" (user/[uid]/edit/tags), на которой он сможет выбрать необходимые значения.

2.0. Использование модуля поиска по отмеченным пользователем тэгам.
Скачайте файл user_tags_search.zip, прикрепленный к этой статье, разархивируйте его в папку /modules, включите модуль user_tags_search через админку (admin/build/modules), в контроле доступа (admin/user/access) раздайте нужным пользователям доступ на 'administer user_tags_search' и 'access user_tags_search'. Укажите словарь, используемый для списка "интересов" в меню настройки модуля (admin/settings/user_tags_search). Здесь надо указать тот же словарь, что и в настройках модуля User Tags (admin/settings/user_tags).

Теперь по адресу http://example.com/users_search должна быть доступна форма поиска пользователей по тэгам, отмеченным в профиле. Форма состоит из двух комбобоксов (select) и одного текстового поля, доступного только для чтения. В первом комбобоксе выводятся термины первого уровня (то есть не имеющие предков) из словаря, выбранного в настройках модуля user_tags_search, второй комбобокс создается пустым, при желании его вообще можно убрать, но по правилам хорошего тона, в создаваемых интерефейсах не должно быть исчезающих-появляющихся объектов.

После выбора в первом комбобоксе категории, во второй комбобокс с помощью xajax подгружаются термины-потомки. После выбора термина во втором комбобоксе он (термин) попадает в текстовое поле, в котором одновременно может находиться сразу несколько терминов. После нажатия кнопки "Поиск" с помощью все тогоже xajax данные передаются на сервер, ведется поиск и клиенту отдается результат.

2.1. Подробное описание модуля.
Модуль состоит из двух файлов: user_tags_search.info, в котороом описаны служебные параметры, и user_tags_search.module, в котором описаны функции, необходимые для работы.

Файл user_tags_search.module состоит всего из 6 функций:

user_tags_search_menu()// – реализация (implementation) функции hook_menu(). Здесь задаются пути (URL), которые будет обрабатывать модуль и функции, которые будут вызываться при запросе пользователем той или иной страницы.

user_tags_search_perm()// – реализация hook_perm(). Этой функцией создаются права доступа к разделам модуля.

user_tags_search_admin_settings()// – функция создающая интерфейс управления модулем.

select_sub_cat($arg)// – функция, обрабатывающая ajax-запрос от клиента. На входе получает tid термина, на выходе отдает новый комбобокс с потомками входящего термина.

select_data($tags)// – функция, обрабатывающая ajax-запрос от клиента. На входе получает массив имен терминов, затем находит tid'ы этих терминов, затем ищет юзеров, которые отметили у себя в профиле эти термины и отдает их список клиенту.

users_search()// – функция, которая рисует пользовательский интерфейс.
?>

Первые три функции не представляют особенного интереса, так как во-первых, их аналоги можно встретить в любом другом модуле, во вторых по их описанию не трудно разобраться как они работают. На последних трех функциях остановлюсь подробнее.

Небольшое отступление.

Логика работы любого ajax-приложения имеет примерно такую структуру:

  • Клиент в браузере выполняет некоторое действие, например заполняет текстовое поле и нажимает кнопку;
  • При нажатии кнопки выполняется java-script функция, которая передает данные на сервер;
  • Сервер обрабатывает полученные данные и передает их браузеру;
  • Браузер отображает данные в заранее определенном для этих целей слое;
  • Пока идет обмен данными между клиентом и сервером пользователь видит на экране браузера сообщение типа "Идет загрузка".

  • Логика работы с библиотекой xajax:

  • На сервере в php-файле создается функция (например my_func()), которая будет обрабатывать клиентский запрос;
  • На клиенте средствами xajax регистрируется функция созданная на предыдущем этапе, после чего xajax автоматически создает java-script функцю вида xajax_my_func(), которая будет передавать данные серверной функции my_func().

  • /*
    Функция рисует пользовательский интерфейс модуля.
    */
    function users_search()
    {
    // подключаю файл, который будет выводить сообщение "Подождите идет загрузка", во время обработки ajax-запроса
    drupal_add_js("xajax_js/xajax_loading.js");
    // подключаю xajax
    require_once('includes/xajax.inc.php' );
    // создаю новый объект
    $xajax = new xajax();
    // регистрирую функции, которые будут передавать данные на сервер
    $xajax->registerFunction("select_sub_cat");
    $xajax->registerFunction("select_data");
    drupal_set_html_head($xajax->getJavascript());
    $xajax->processRequests();

    // создаю форму поиска
    $content .= "\n";
    $content .= "

    \n";
    $content .= "

    ";

    // Запуск поиска при клике по соответствующей кнопке
    $content .= "

    ";
    $content .= "

    ";

    // при выделении элемента комбобокса, выполняется функция, посылающая серверу запрос на получение комбобокса с потомками
    $content .= "\n";
    $content .= "Выберите категорию: ";

    // заполняю первый комбобокс терминами, у которых нет предков
    foreach (taxonomy_get_tree(variable_get('user_tags_search_root', '')) as $tid => $term)
    {
    if($term->depth == 0)$content .= "tid . "\">" . $term->name . "\n";
    }
    $content .= "

    ";

    // Создаю слой, в котором будет выводиться комбобокс с терминами-потомками. Теоретически можно не создавать в этом слое комбобокс, то есть просто использовать код типа , но, как я уже писал выше, по правилам хорошего тона, лучше не создавать интерфейсы с исчезающими-появляющимися контролами.
    $content .= "<-- Выберите категорию";
    $content .= "

    ";
    $content .= "Выберите категорию в первом списке, а затем один или несколько тэгов во втором, нажмите кнопку \"Поиск\" и система найдет людей, которые отметили выбранные вами тэги.\n";

    // Этот слой не отображается по умолчанию, а выводится только во время обмена данными между клиентом и сервером
    $content .= "
    Подождите, идет загрузка данных

    ";
    $content .= "\n

    ";
    $content .= "";
    $content .= "";

    return $content;
    }

    /*
    Выбор терминов-потомков
    */
    function select_sub_cat($arg)
    {
    // инициализация объекта xajax
    $objResponse = new xajaxResponse();
    $output = "";
    $output .= "Выберите тэг:";

    // выбор данныих из базы
    $my_r = db_query("SELECT t.tid, t.name from {term_data} t INNER JOIN {term_hierarchy} h ON t.tid = h.tid WHERE h.parent = '" . $arg . "' ORDER BY t.name ASC");

    while($my_obj = db_fetch_object($my_r))
    {
    $output .= "name . "'>" . $my_obj->name . "\n";
    }
    $output .= "";

    // передача данных клиенту в слой updatableDiv, который создан в функции users_search
    $objResponse->addAssign('updatableDiv', 'innerHTML', $output);

    return $objResponse->getXML();
    }
    ?>

    Функция select_data($tags) имеет немного более сложный алгоритм выбора данных, но с точки зрения работы с клиентским скриптом она абсолютно идентична предыдущей функции, по этому подробнее на ней я останавливаться не буду.

    ВложениеРазмер
    Иконка пакета xajax_0.2.4.zip70.89 КБ
    Иконка простого текстового файла xajax_loading.js_.txt301 байт
    Иконка пакета user_tags_search.zip3.79 КБ

    Комментарии

    Аватар пользователя blackvl@drupal.org blackvl@drupal.org 23 мая 2007 в 12:33

    1. буквоедство Smile
    return $objResponse->getXML();
    в 2.4 уже можно просто return $objResponse;
    2. Проблем с отображением русского текста не возникает? (с 1251 приходилось изворачиваться...)

    Аватар пользователя blackvl@drupal.org blackvl@drupal.org 23 мая 2007 в 12:51

    Отличие как у терки и кухонного комбайна Smile
    jquery - всего лишь javascript библиотека
    xajax это целое api, очень удобное в использовании совместно с пхп, что Ромка и продемонстрировал
    Обратите внимание на

    // регистрирую функции, которые будут передавать данные на сервер
          $xajax->registerFunction("select_sub_cat");
          $xajax->registerFunction("select_data");
          drupal_set_html_head($xajax->getJavascript());
          $xajax->processRequests();

    Весь фокус в том, что вы пишете на пхп функцию, а работает она как javascript, т.е. в принципе вы можете не знать всех тонкостей javascript - за вас это сделает xajax.

    Аватар пользователя Ромка Ромка 23 мая 2007 в 14:23

    blackvl@drupal.org
    2. Проблем с отображением русского текста не возникает? (с 1251 приходилось изворачиваться...)
    В друпале – нет, не возникает, так как xajax, как и Друпал, по умолчанию работает в UTF-8, но, при желании, кодировку можно исправить на любую другую (переменная XAJAX_DEFAULT_CHAR_ENCODING в файле xajax.inc.php)

    Аватар пользователя Dan Dan 23 мая 2007 в 14:39

    > Чем библиотека xajax лучше библиотеки jQuery, которая уже есть в ядре?
    Из AJAX в ядре есть только автозавершение строк ввода, имхо.

    Аватар пользователя axel axel 27 мая 2007 в 18:02

    Да, нет почему, использовать xmlhttprequest никто не мешает. А jquery она немного для других целей - просто позволяет писать более короткий код на javascript. В друпале немало модулей использующих xmlhttprequest - пройдись rgrep по CVS contributions/modules/ на предмет xmlhttprequest, увидишь. Но описанный способ применения xajax мне больше понравился, поскольку можно не особо вдаваться в подробности javascript.

    Аватар пользователя rapitosov@drupal.org rapitosov@drupal.org 28 мая 2007 в 17:17

    посмотри на http://visualjquery.com/1.1.1.html там в самой левой колонке список категорий функций, который дает некоторое представление для чего используется таки jquery:

    DOM - для работы с объектной моделью документа: выборки и изменения атрибутов, манипуляции деревом объектов, поиска объектов.
    CSS - для выборки и изменения каскадных свойств объекта.
    Events - для прицепки к событиям
    Effects - для проигрывания стандартных эффектов и комбинирования из них более сложных
    Ajax - для динамического обновления части объектного дерева

    это не считая плагинов

    При этом сама библиотека спроектирована так, что бы большинство часто употребимых действий, требующих написания 10-20 строчной фукции, выполнялись вызовом(!) одной двух функций. По аналогии с, например, drupal_set_message(), который избавляет разработчика от необходимости писать код для записи в базу сообщений и последующего вывода его пользователю. Плюс аналогия с юниксовым пайпом - передача объекта к следующей в очереди функции:

    $('.picture').children().children().css("width","150px"); находит объект с классом picture, который передается по очереди двум функциям получающим чайлд, и функции изменяющей каскадный аттрибут ширины (у кого в фаерфоксе установлен Firebug могут попробовать этот код в Console, все остальные обязаны срочно поставить, я проверю Smile

    Вобщем всячески рекомендую, немного тяжело въехать поначалу, но потом невозможно оторваться.

    Аватар пользователя Ромка Ромка 24 мая 2007 в 14:00

    кстати, это модуль не показывает количества людей с такими ж интересами (как например на beta.ya.ru)

    Я просто не посчитал эту функцию нужной, хотя счетчик там есть. Достаточно в коде модуля после строки
    <?php
    if($counter == 0)$content .= "поиск не принес результата";
    ?>Добавить строчку типа
    <?php
    else $content .= "найдено " . $counter . "пользователей с выбранными тэгами";
    ?>

    Аватар пользователя Ромка Ромка 24 мая 2007 в 14:12

    Хотя нет, как написал в предыдущем посте лучше не делать. Модифицировал код и выложил новую версию модуля, теперь он выводит инфу о количестве найденных юзеров.

    Вообще я этот модуль рассматривал именно как пример работы с xajax, а не как полноценный модуль, если кому-то он покажется интересным могу довести его до ума, сделать темизацию итп.

    Аватар пользователя Demon Demon 26 мая 2007 в 13:59

    У меня лично с xajax были проблемы в одном из проектов. Мне пришлось эту библиотеку немного модифицировать. Дело вот в том, что xajax переопределяет стандартный тип Array(), добавляя в него новый функционал. И некоторые другие javascript библиотеки из-за этого работают некорректно. Встречается конечно такая проблема редко, но встречается.

    Аватар пользователя axel axel 27 мая 2007 в 18:04

    Вероятно для красоты примера генерацию формы стоило бы сделать через друпаловское API, но это уже так, придирки Smile В целом описание очень полезное, спасибо! Попробую применить xajax в рабочем проекте.

    Аватар пользователя selff selff 27 мая 2007 в 20:27

    извините, что не в тему, но название сайта повеселило:
    "drugme" - можно перевести как - дайте мне наркотиков.

    а поводу модуля - класс!
    правда не ясно будет ли работать модуль в древовидном многоуровневом словаре?

    Аватар пользователя Ромка Ромка 27 мая 2007 в 22:10

    Вероятно для красоты примера генерацию формы стоило бы сделать через друпаловское API
    Согласен, возможно, на досуге переделаю этот кусок кода. Просто основной акцент я хотел сделать на том насколько удобен в использовании xajax, а не на возможностях Друпала.

    а поводу модуля - класс!
    правда не ясно будет ли работать модуль в древовидном многоуровневом словаре?

    Нет, не будет. Точнее будет, но в любом случае в первый селект будет подгружать только термины из конря словаря, во второй селект – только термины-потомки термина выбранного в первом селекте, а термины находящиеся на большей глубине выводиться не будут. При желании скрипт можно модифицировать для работы с любой другой глубиной вложенности. Или можно вообще не париться и выводить все термины в одном комбобоксе, но тогда в этом примере просто не на чем будет показывать работу с xajax.

    Аватар пользователя Ромка Ромка 11 июня 2007 в 10:23

    Блин, поразбирался с jQuery – шиикарная штука!

    Во-первых это не чисто библиотека для работы с AJAX, как xajax, кроме того это простой и мощный инструмент для разработки ява-скрипт украшательств для страницы.

    Во-вторых и работа с самим аяксом в этой библиоетеке, имхо, реализована логичнее. В xajax на первом этапе ява-скрипт функция отдаёт запрос серверу и далее, на втором этапе, серверная часть с помощью автоматически сгенерированного xajax'ом яваскрипта обновляет нужные данные. В jQuery оба этапа обрабатываются программистом самостоятельно, что, теоретически, должно сделать работу более гибкой.

    Ну и, в третьих, весит это чудо всего 20 Кб.

    Так что я сейчас доделаю один свой проект, в котором применяю jQuery и поделюсь впечатлениями.

    P.S. Кстати, с Друпалом 5.1 идет не последняя версия jQuery, я на этом полчаса где-то потерял. пытаясь понять, почему не работает примитивный, вроде бы, скрипт.

    Аватар пользователя Maximark Maximark (не проверено) 22 июня 2007 в 13:54

    За что мне нравиться Drupal, за то что код "шаблонов" не отделен кода cms....

    ну что это за ерунда..

    $content .=

    ну неужеле нельзя отделить код просмотра от выполняемого кода ! Потом чтобы изменить тему , шаблон и т п нужно лазить по всем модулям и искать
    $content .= ну это маразм чистой воды.

    А если апгрейд модуля идет...?

    Аватар пользователя Ромка Ромка 23 июня 2007 в 14:23

    Собственно это не недостаток Друпала, а недостаток моего примера. Аксель выше уже обращал внимание на то, что форму лучше генерить Друпальскими средствами, а затем темизировать. Просто руки не дошли поправить пример.

    Да и основной целью примера было показать возможности xajax (в котором я, кстати, уже немного разочаровлся, jquery рулит!), а не возможности Друпала по созданию форм...