• статья
  • pantey

Создаем раскрытый ajax фильтр без применения views

16.04.2015

Думаю многие из вас не раз создавали «раскрытый» фильтр в Drupal 7 через views, кто не в курсе, что это и как это создается идем сюда. Данные фильтры помогают нам отфильтровывать ноды, по каким либо параметрам, сейчас же я вам покажу, как самостоятельно можно написать данный фильтр без создания отдельной вьюхи.

Для начала давайте определимся с задачей. Например, создадим кастомную форму, которая должна формировать селект из всех имеющихся у нас типов материалов в системе и кнопку отправки формы. Т.е. мы будем отфильтровывать ноды по типу материала, кроме того усложним задачу и отфильтровывать материалы мы будем через ajax, а так же выведем данную форму в блок.

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

  1. Создаем кастомную форму с селектом из типов материалов, существующие в системе и кнопку отправки формы.
  2. Создаем блок и выводим форму в блок.
  3. Пишем запросы в БД, исходя из значений фильтра выбранные пользователем

Все действия мы естественно будем производить в своем модуле, как создать свой модуль – читаем здесь. В данном случае, мой модуль я назову – myexposed_filter.

Создаем форму с селектом из типов материалов и кнопку отправки формы

Как создаются простейшие формы в Drupal 7 - можно прочитать здесь, я на этом не буду останавливаться.

  1. <?php
  2. /**
  3.  * Implements hook_form
  4.  */
  5. function myexposed_filter_form($form, &$form_state){
  6.  
  7. // Получаем список типов материалов в системе
  8. $type_content = node_type_get_types();
  9. $options_type = array();
  10.  
  11. // Записали в массив типы материалов, где ключами является машинное имя типа материала
  12. foreach($type_content as $value){
  13. if($value->orig_type != 'page'){
  14. $options_type[$value->orig_type] = $value->name;
  15. }
  16. }
  17.  
  18. // Селект с типом материала
  19. $form['type'] = array(
  20. '#type' => 'select',
  21. '#title' => t('Select content type'),
  22. '#options' => $options_type,
  23. '#empty_option' => '--Select type content--',
  24. );
  25.  
  26. // Кнопка отправки формы
  27. $form['submit'] = array(
  28. '#type' => 'submit',
  29. '#value' => t('Filter'),
  30. '#ajax' => array(
  31. 'callback' => 'myexposed_filter_callback',
  32. 'wrapper' => 'myexposed-filter-form',
  33. ),
  34. );
  35.  
  36. return $form;
  37.  
  38. }

Хочу заметить, что для кнопки формы мы для Ajax, в качестве келбека отдали функцию myexposed_filter_callback – данную функцию мы опишем ниже

Хочу заметить, что мы убрали из списка типов материалов тип - Страница, ибо формат вывода нод - тизер, а данный тип не имеет тизера.

Создаем блок и выводим форму в блок
  1. <?php
  2. /**
  3.  * Implements_hook_block_info
  4.  */
  5. function myexposed_filter_block_info(){
  6.  
  7. $blocks = array();
  8. // Описали свой блок с фильтром
  9. $blocks['myexposed_filter'] = array(
  10. 'info' => t('My exposed filter'),
  11. );
  12.  
  13. return $blocks;
  14.  
  15. }
  16.  
  17. /**
  18.  * Implements_hook_block_view
  19.  */
  20. function myexposed_filter_block_view($delta=''){
  21.  
  22. $block = array();
  23. if($delta == 'myexposed_filter' ){
  24. // Заголовок блока фильтра
  25. $block['subject'] = t('My exposed filter');
  26. // Получили форму с указанным названием
  27. $block['content'] = drupal_get_form('myexposed_filter_form');
  28. }
  29.  
  30. return $block;
  31. }
Пишем запросы в БД, исходя из значений фильтра выбранные пользователем
  1. <?php
  2. /**
  3.  * Callback ajax submit
  4.  */
  5. function myexposed_filter_callback($form, $form_state){
  6.  
  7. // Получили значения фильтра
  8. $type_content = $form_state['values']['type'];
  9.  
  10. // Проверяем не пустые ли данные значения
  11. if(!empty($type_content)){
  12.  
  13. $nodes = node_load_multiple(array(), array('type' => $type_content));
  14. // Получили список нод для указанного типа
  15. $views = node_view_multiple($nodes, 'teaser');
  16. $output = drupal_render($views);
  17.  
  18. }else{
  19.  
  20. // Получаем NID нод, которые опубликованы и выводятся на главной странице сайта
  21. $nids = db_select('node', 'n')
  22. ->fields('n', array('nid'))
  23. ->condition('status', 1)
  24. ->condition('promote', 1)
  25. ->orderBy('n.created', 'DESC')
  26. ->extend('PagerDefault')
  27. ->limit(10)
  28. ->execute();
  29.  
  30. // Формируем массив с NID нод
  31. foreach ($nids as $nid) {
  32. $items[] = $nid->nid;
  33. }
  34.  
  35. // Загружаем объекты нод и формирусем формат вывода ноды
  36. $nodes = node_load_multiple($items);
  37. $views = node_view_multiple($nodes, 'teaser');
  38.  
  39. // Рендерим список нод и добавляем пагинацию
  40. $output = drupal_render($views) .theme('pager');
  41.  
  42. }
  43.  
  44. // Ajax команды
  45. $commands = array();
  46. // Изменяем содержимое DOM элемента с указаным ID - на отрендеренный список нод
  47. $commands[] = ajax_command_html('#block-system-main', $output);
  48. return array('#type' => 'ajax', '#commands' => $commands);
  49.  
  50. }
Полный код модуля
  1. <?php
  2.  
  3. /**
  4.  * Implements_hook_block_info
  5.  */
  6. function myexposed_filter_block_info(){
  7.  
  8. $blocks = array();
  9. // Описали свой блок с фильтром
  10. $blocks['myexposed_filter'] = array(
  11. 'info' => t('My exposed filter'),
  12. );
  13.  
  14. return $blocks;
  15.  
  16. }
  17.  
  18. /**
  19.  * Implements_hook_block_view
  20.  */
  21. function myexposed_filter_block_view($delta=''){
  22.  
  23. $block = array();
  24. if($delta == 'myexposed_filter' ){
  25. // Заголовок блока фильтра
  26. $block['subject'] = t('My exposed filter');
  27. // Получили форму с указанным названием
  28. $block['content'] = drupal_get_form('myexposed_filter_form');
  29. }
  30.  
  31. return $block;
  32. }
  33.  
  34. /**
  35.  * Implements hook_form
  36.  */
  37. function myexposed_filter_form($form, &$form_state){
  38.  
  39. // Получаем список типов материалов в системе
  40. $type_content = node_type_get_types();
  41. $options_type = array();
  42.  
  43. // Записали в массив типы материалов, где ключами является машинное имя типа материала
  44. foreach($type_content as $value){
  45. if($value->orig_type != 'page'){
  46. $options_type[$value->orig_type] = $value->name;
  47. }
  48. }
  49.  
  50. // Селект с типом материала
  51. $form['type'] = array(
  52. '#type' => 'select',
  53. '#title' => t('Select content type'),
  54. '#options' => $options_type,
  55. '#empty_option' => '--Select type content--',
  56. );
  57.  
  58. // Кнопка отправки формы
  59. $form['submit'] = array(
  60. '#type' => 'submit',
  61. '#value' => t('Filter'),
  62. '#ajax' => array(
  63. 'callback' => 'myexposed_filter_callback',
  64. 'wrapper' => 'myexposed-filter-form',
  65. ),
  66. );
  67.  
  68. return $form;
  69.  
  70. }
  71.  
  72. /**
  73.  * Callback ajax submit
  74.  */
  75. function myexposed_filter_callback($form, $form_state){
  76.  
  77. // Получили значения фильтра
  78. $type_content = $form_state['values']['type'];
  79.  
  80. // Проверяем не пустые ли данные значения
  81. if(!empty($type_content)){
  82.  
  83. $nodes = node_load_multiple(array(), array('type' => $type_content));
  84. // Получили список нод для указанного типа
  85. $views = node_view_multiple($nodes, 'teaser');
  86. $output = drupal_render($views);
  87.  
  88. }else{
  89.  
  90. // Получаем NID нод, которые опубликованы и выводятся на главной странице сайта
  91. $nids = db_select('node', 'n')
  92. ->fields('n', array('nid'))
  93. ->condition('status', 1)
  94. ->condition('promote', 1)
  95. ->orderBy('n.created', 'DESC')
  96. ->extend('PagerDefault')
  97. ->limit(10)
  98. ->execute();
  99.  
  100. // Формируем массив с NID нод
  101. foreach ($nids as $nid) {
  102. $items[] = $nid->nid;
  103. }
  104.  
  105. // Загружаем объекты нод и формирусем формат вывода ноды
  106. $nodes = node_load_multiple($items);
  107. $views = node_view_multiple($nodes, 'teaser');
  108.  
  109. // Рендерим список нод и добавляем пагинацию
  110. $output = drupal_render($views) .theme('pager');
  111.  
  112. }
  113.  
  114. // Ajax команды
  115. $commands = array();
  116. // Изменяем содержимое DOM элемента с указаным ID - на отрендеренный список нод
  117. $commands[] = ajax_command_html('#block-system-main', $output);
  118. return array('#type' => 'ajax', '#commands' => $commands);
  119.  
  120. }

Теперь, вы можете активировать ваш модуль, вывести блок с раскрытым фильтром в необходимый вам регион и смотреть как все работает. Думаю, из комментариев в коде, все должно быть понятно.

Результат работы фильтры:

Результат работы фильтра

Скачать готовый модуль можно здесь.

-->
Узнавай о новых статьях сайта - первым. Просто подпишись на рассылку.

Комментарии (26)

Profile picture for user Astral
Дмитрий
16.04.2015

Круто! Спасибо за модуль!
Кстати на мой взгляд было б еще круче если б еще при выборке в адресной строке прописывался путь с переменными типа
www.mysite.ru/?type=page&title=как заработать миллион

Profile picture for user pantey
pantey
16.04.2015

опишите свою страницу через hook_menu, которая будет получать значения формы через get, и удалите Ajax. Получите то, что хотите.

Profile picture for user Astral
Дмитрий
16.04.2015

эт понятно! Но я имел ввиду, чтоб ajax оставался

Profile picture for user Astral
Дмитрий
16.04.2015

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

Profile picture for user pantey
pantey
16.04.2015

именно так - не возможно и не понятно вообще для чего это нужно.

Profile picture for user Astral
Дмитрий
16.04.2015

Для того, чтоб пользователь смог скопировать отфильтрованную ссылку и передать/отправить ее кому нибудь.

Profile picture for user pantey
pantey
16.04.2015

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

Profile picture for user Astral
Гость
20.04.2015

Я думаю менять содержимое в #block-system-main не совсем корректно, можно что-нибуть лишнее захватить. Да и как минимум content еще надо добавить, а то все стили могут послетать.

Profile picture for user pantey
pantey
20.04.2015

ну куда именно загружать результат фильтра все таки зависит от темы, так же как и класс content - это сугубо личное дело каждого. Применять стили к content - не есть гут, ибо он используется Drupal везде, где происходит рендер контента. А так для bartik, замечание имеет смысл.

Profile picture for user Astral
Николай
23.04.2015

А можно продолжение статьи сделать о создании нескольких фильтров, среди которых есть поля term reference с несколькими уровнями терминов (например: -Audi, -- A4, -- A6, - BMW, --X5, --X6 итд), думаю будет полезно и интересно :)

Profile picture for user Astral
Николай
09.06.2015

Здрасти!
Как заставить работать ajax в раскрытых формах в блоке?

Profile picture for user pantey
pantey
09.06.2015

так же как и для страниц, отличий в ajax нет.

Profile picture for user Astral
Николай
09.06.2015

помогите понять пожалуйста..img

Profile picture for user Astral
Николай
09.06.2015

у меня есть несколько раскрытых форм с чекбоксами и выбором цены по диапазону в фильтре в раскрытом блоке, все хорошо, но есть одно но, я не хочу чтоб при выборе или отправки перезагружалась стр., я хочу чтоб стр. работала на ajax, как мне это реализовать?

Profile picture for user Astral
Николай
09.06.2015

а почему аякс в формах работает только в содержимом а не в блоке?

Profile picture for user pantey
pantey
09.06.2015

Ajax работает с любым DOM элементом страницы. В drupal есть свои ajax команды, в частности команда

ajax_command_replace('.test', 'текст для замены');

выберет элемент с классом test и заменит его на текст "текст для замены". Только вы определяете, что на что менять.

Profile picture for user Astral
Николай
09.06.2015

тогда почему когда я переключаю во view галочку на "Раскрытая форма в блоке:Да", форма появляется в регионе но аякс не работает?

Profile picture for user pantey
pantey
09.06.2015

так и сказали бы, что используете view. Не думаю, что при таком раскладе должен срабатывать Ajax в views. Хотя нужно посмотреть.

Profile picture for user Astral
Николай
09.06.2015

я так понимаю для того чтоб он заработал надо писать модуль?

Profile picture for user Astral
Николай
15.07.2015

Здравствуйте!
Как сделать во view расположение view-filters и view-content в разных регионах?
view-filters в левой колонке а view-content соответственно оставить как есть.

Profile picture for user pantey
pantey
15.07.2015

в настройках views, поставить галочку напротив использовать "Использовать раскрытую форму в блоке" и далее этот блок вывести в необходимом вам регионе.

Profile picture for user Astral
Николай
15.07.2015

Да спасибо, я в курсе, у меня так и сделано но мне нужно чтоб стр. работала без перезагрузки по ajax, ajax соответственно вкл.

Profile picture for user pantey
pantey
15.07.2015

по идеи тогда должен работать и ajax. Посмотрите, сработает ли ajax на базовой теме.

Profile picture for user Astral
Николай
15.07.2015

У меня две вьюхи:
1 каталог (catalog/%) где выводится только продукты
2 фильтр (filter) с настройками раскрытых фильтров в блоке
все работает и фильтруется, но с перезагрузкой на стр. (filter) а вот ajax не пашит ((

Profile picture for user Astral
Николай
15.07.2015

Но если я выключаю view с каталогом и добавляю отображение блок к view с фильтрами page и вывожу этот блок в содержимое, то ajax работает, но весь вывод получается в регионе содержимого а мне нужно чтоб фильтры были в левой колонке а продукты в правой тобишь в регионе содержимого

Profile picture for user Astral
Николай
15.07.2015

Вообщем удалил я view filter и оставил только view catalog с видом page, раскрыл все фильтры, вывел их через better_exposed_filters, что в левой колонке что в содержимом ajax не работает, менял на базовые тему, без изменений, в чем может быть дело???