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: Drupal, jQuery, PHP
Category:
Drupal |
6 Comments »
21 November 2012 - 19:33
Спасибо за статью.Очень полезная вещь, пригодится для любого магазина, фильтр в правой колонке магазина тоже понравился)
10 February 2013 - 21:57
А можно узнать как реализован поиск по части слова на reactive-shop.ru?
11 February 2013 - 9:04
Когда мы настраивали в нашем view критерии фильтра, то указали оператор “Contains”, который подразумевает поиск по части слова.
9 February 2014 - 12:37
По идее это ведь можно реализовать с помощью обычного поиска с подгрузкой результатов по ajax. Только блок с результатами позиционировать абсолютно относительнhttp://sweetcaptcha.s3.amazonaws.com/widget/v2/upload/answer_121.pngо формы поиска
28 August 2014 - 22:44
А как сделать чтобы курсор после всплывания результатов поиска не исчезал, а оставался в поле поиска?
15 August 2020 - 16:44
i was reading this https://1xslots-africa.site