Drupal: ajax-автодополнение поиска

Date January 3rd, 2012 Author Vitaly Agapov


Правда иной раз гнётся, но никогда не ломается и всплывает поверх лжи, как масло — поверх воды.

– Мигель де Сервантес Сааведра

Как-то раз я уже писал про Ajax-автозаполнение (Ajax-autosuggest) своими силами. Там в комментариях было много возмущения из-за того, что никак не была рассмотрена серверная часть решения. Причины, по которым я о ней не писал, ясны: это абсолютное разнообразие возможных серверных решений. Например, мой Perl’овый cgi-скрипт никак не прояснил бы ситуацию человеку, работающему с PHP. А пример с серверной частью в виде модуля для Drupal совсем не поможет адепту Joomla. Но теперь моя совесть будет совсем чиста, так как я всё-таки опишу, как самостоятельно и с наименьшими трудозатратами сделать автодополнение для стандартного поиска в Drupal 7.

Конечно же, можно использовать любой модуль из целой россыпи соответствующих расширений для Drupal: Finder, AutoSuggest Search и многих других.  Но тут либо нет портированной версии для Drupal 7.x, либо в модуле нет нужной нам функциональности, либо он работает не совсем так, как мы хотим, либо просто у нас чешутся руки. В общем, хотим сделать по-своему. Хотя стоит отметить, что Finder очень и очень хорош.

Подготовительный этап

Для начала нам нужен div, в который будет выводиться результат поиска. Его можно добавить хуком hook_block_view(), можно сделать свой шаблон search-block-form.tpl.php, можно добавить объект с помощью javascript, или самое простое – добавить этот div прямо в свой шаблон page.tpl.php. В общем, добавляем в разметку страницы:

<div id=”ajaxsearchresult” style=”display:none;”>

Создание View

Можно, конечно, из своего модуля обращаться к базе напрямую и выбирать нужные нам значения по нужному нам фильтру, но мы договорились делать всё наиболее простым способом. Поэтому модуль Views – наш выбор. Его обвиняют в тяжести и медлительности, но зато он умеет делать всё, что нам может потребоваться для выборки результатов, и даже больше.

Наш view мы назовём ajaxfinder. Создадим путь к нему /ajaxfinder.  Формат – таблица.

Список полей – по своему усмотрению. Так как в моём случае Drupal работает с модулем Ubercart, то и набор полей соответствующий: изображение стиля uc_thumbnail (со значением “Display 1 value” в блоке “Multiple Field Settings”, чтобы выводить только одно изображение, если у ноды их несколько), заголовок и стоимость для продажи.

Фильтр – по опубликованным товарам и по самому главному критерию, заголовку.В свойствах фильтра по заголовку надо разрешить изменение критерия (Expose this filter to visitors) и указать оператор Contains или Starts With. Так мы сможем выбирать нужные ноды, указывая значение фильтра как атрибут запроса к странице. Имя атрибута будет соответствовать значению поля Filter identifier. В моём случае это title.

На данном этапе после сохранения View на странице mysitename.ru/ajaxfinder?title=test должны выводиться соответствующие ноды в соответствующем табличном виде.

Создание модуля

Всё остальное будет делать модуль. Назовём его ajaxsearch. В нём будут всего три файла: ajaxsearch.info, ajaxsearch.module и ajaxsearch.js. Если что, то положить их надо будет в директории ajaxsearch в sites/all/modules/.

ajaxsearch.info

name = "AJAX Search"
description = "AJAX autosuggest for products"
core = 7.x
files[] = ajaxsearch.module
scripts[] = ajaxsearch.js

ajaxsearch.module

<?php
/**
 * Implements hook_menu().
 */
function ajaxsearch_menu() {
  $items = array();
  $items['ajaxsearch/getblock'] = array(
    'page callback' => 'ajaxsearch_get_block',
    'access arguments' => array('access content'),
    'type' => MENU_CALLBACK,
  );
  return $items;
}
function ajaxsearch_get_block( $s = '' ) {
    $viewName='ajaxfinder';
    $displayId='default';
    $view = views_get_view($viewName);
    $view->set_display($displayId);
    $view->set_exposed_input(array('title' => $s));
    $view->execute();
    $res = $view->preview();
    return drupal_json_output(array('products'=>$res));
    exit;
}
?>

Здесь реализация hook_menu создаёт адрес ajaxsearch/getblock, контекстные аргументы которой передаются в callback-функцию ajaxsearch_get_block. Эта функция уже получает View и отдаёт его в JSON-формате.

ajaxsearch.js

  var suggest_count = 0;
  Drupal.behaviors.ajaxsearch = {
    attach: function(context) {
        jQuery('*:not(#ajaxsearchresult)').click(function() {
                jQuery('#ajaxsearchresult', context).fadeOut();
        });
        jQuery('#ajaxsearchresult tr').click(function() {
                document.location = jQuery(this).find('a').attr('href');
        });
        jQuery('#block-search-form input', context).bind('keyup', function() {
                var s = jQuery(this).val();
                suggest_count++;
                setTimeout("searchGo("+suggest_count+")",300);

        });
    }
  };
  function searchGo(count) {
        if ( count == suggest_count ) {
                var s = jQuery('#block-search-form input').val();
                if (! s) { jQuery('#ajaxsearchresult').hide(); return; }
                var updateBlock = function(data) {
                        jQuery('#ajaxsearchresult').html(data.products);
                        if ( jQuery('#ajaxsearchresult .view-content').length > 0 ) 
                                jQuery("#ajaxsearchresult").show();
                                else jQuery("#ajaxsearchresult").hide();
                        Drupal.attachBehaviors('#ajaxsearchresult');
                }
                jQuery.ajax({
                   type: 'POST',
                   url: '/ajaxsearch/getblock/'+s,
                   success: updateBlock, 
                   dataType: 'json', 
                   data: 'js=1' 
                });
        }
  }

Это очень упрощённый для наглядности вариант скрипта с минимумом необходимой функциональности. При желании сделать полноценное решение можно обратиться к вышеупомянутой статье Ajax-автозаполнение (Ajax-autosuggest) своими силами.

Здесь же к объекту ‘#block-search-form input’ привязывается обработчик нажатия клавиш. Он ждёт 300 мс и, если новых нажатий за это время не было, то функция searchGo отправляет POST-запрос к нашей странице /ajaxsearch/getblock/ с соответствующим контекстным аргументом. Возвращаемое значение присваивается объекту ‘#ajaxsearchresult’, после чего тот отображается jQuery-методом show.

Не забыта необходимость закрыть всплывающее окно при клике в любую часть страницы и необходимость сделать переход на страницу ноды при клике на любую часть строки таблицы.

CSS

Само собой, надо стилизовать всплывающее окно. Тут полная свобода творчества, но две вещи обязательны:

Надо задать позиционирование блока:

#ajaxsearchresult { position: absolute; z-index:100; }

И спрятать форму для ручного ввода значения фильтра:

#ajaxsearchresult .view-filters { display: none; }

Конец

На этом всё. Отдыхаем, пьём морс.
Посмотреть рабочий вариант можно по адресу reactive-shop.ru.

Tags: , ,
Category: Drupal | 5 Comments »

Comments

5 комментариев на “Drupal: ajax-автодополнение поиска”

  1. derial

    Спасибо за статью.Очень полезная вещь, пригодится для любого магазина, фильтр в правой колонке магазина тоже понравился)

  2. Alex

    А можно узнать как реализован поиск по части слова на reactive-shop.ru?

  3. Vitaly Agapov

    Когда мы настраивали в нашем view критерии фильтра, то указали оператор “Contains”, который подразумевает поиск по части слова.

  4. Евгений

    По идее это ведь можно реализовать с помощью обычного поиска с подгрузкой результатов по ajax. Только блок с результатами позиционировать абсолютно относительнhttp://sweetcaptcha.s3.amazonaws.com/widget/v2/upload/answer_121.pngо формы поиска

  5. kstu

    А как сделать чтобы курсор после всплывания результатов поиска не исчезал, а оставался в поле поиска?

Leave a comment

 Comment Form