Как создать продвинутый поиск в WordPress с помощью WP_Query

Как создать продвинутый поиск в WordPress с помощью WP_Query

От автора: в этой статье я покажу вам, как добавить в WordPress продвинутый поиск, с помощью которого пользователи смогут искать и получать контент из определенных постов пользовательского типа, фильтровать результаты поиска по пользовательским таксономиям и множеству других пользовательских полей.

Большая часть преимуществ WordPress идет из его гибкой архитектуры данных, с помощью которой разработчики могут широко настраивать свои системы с помощью пользовательских типов постов, таксономий, а также пользовательских полей. Однако когда речь заходит о поиске в WordPress, то пользователю предоставляется всего одно поле, которое зачастую ведет себя настолько неадекватно, что администраторы загружают внешние поисковые системы наподобие Google Custom Search или сторонних плагинов.

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

Запросы пользователя

Когда пользователь переходит по ссылке или вбивает URL определенной страницы вручную, WordPress выполняет серию операций, хорошо описанную в Query Overview. Если коротко, то происходит следующее:

WordPress разбирает запрашиваемый URL на набор параметров (которые называются спецификация запроса).

Задаются все переменные «is_», связанные с запросом.

Спецификация запроса конвертируется в MySQL запрос, который посылается в базу данных.

Полученные из БД данные хранятся в объекте $wp_query.

Затем WP обрабатывает ошибку 404.

Посылаются HTTP заголовки блога.

Инициализируются переменные цикла.

По правилам иерархии выбирается шаблон.

WordPress запускает цикл.

Первым происходит парсинг URL строки, так что давайте разберем строки запроса и переменные.

Переменные WP Query: стандартные и пользовательские

В кодексе говорится: «Пользователи или разработчики WordPress могут использовать массив переменных запроса для поиска контента определенного типа или в качестве дополнительного функционала в дизайне темы и/или плагина.»

Другими словами, переменные запроса WP определяют результат выполнения запроса в БД. По умолчанию в WP доступны переменные запроса типа private и public. В кодексе о таких переменных говорится: Публичные переменные запроса это переменные, доступные напрямую через URL запроса из формы. example.net/?var1=value1&var2=value2

Приватные переменные нельзя использовать в URL строке. WP принимает строку запроса с приватными переменными, но они не пройдут парсинг. Поэтому такие переменные необходимо указывать напрямую в запросе. Пример ниже.

И как следствие, через строку запроса нельзя послать такие приватные переменные, как category__in, category__not_in, category__end и т.д (в кодексе вы найдете полный список встроенных переменных, которые можно использовать в строке запроса).

С помощью публичных переменных мы (как пользователи или разработчики) можем составить довольно много запросов, и нам не понадобится создавать плагины или редактировать файл темы functions.Нам нужно всего лишь создать URL, добавить к строке запроса пару доступных параметров, и WP покажет пользователю страницу с результатом.

В качестве примера с помощью параметра post_type в строке запроса мы можем получить посты определенного типа; или мы можем произвести поиск по пользовательской таксономии, добавив в конец строки запроса пару taxonomy-name=taxonomy-term. К примеру, можно создать следующий URL: mywebsite.com/?post_type=movie&genre=thriller

WP обратиться к базе данных и вытянет все посты типа movie с жанром thriller, где genre – пользовательская таксономия. Круто, но это не все. До сих пор мы говорили только о встроенном функционале переменных запроса, однако WordPress так устроен, что мы можем создавать свои собственные переменные запроса.

Как зарегистрировать пользовательские переменные запроса

Пользовательские переменные необходимо зарегистрировать перед использованием. Сделать это можно с помощью фильтра query_vars. Давайте откроем главный файл плагина (или файл темы functions.php) и пропишем там код ниже:

Колбэк функция принимает массив переменных в качестве аргумента и должна вернуть такой же массив после добавления новых переменных.

Теперь можно добавить наши новые переменные в параметры строки запроса. Благодаря get_query_var() эти параметры будут доступны в нашем скрипте, это мы увидим чуть позже. А сейчас настало время познакомить вас с классом, который управляет запросами в WordPress.

Ее величество WP_Query

Запрос в базу данных – задача не из простых. Главное не просто создать точный запрос, а создать запрос таким, чтобы в нем не было дыр в безопасности. С помощью WP_Query Class мы можем получить быстрый и безопасный доступ к БД WordPress (WP_Query строит безопасные запросы в фоновом режиме).

С помощью множества методов и свойств класса полученные из БД данные попадают в цикл Loop. Разберем базовый пример с Loop:

Если вы еще новичок в WordPress разработке, вы можете задаться вопросом: «А где, собственно, сам запрос?!». По факту, вам не нужно создавать новый экземпляр объекта WP_Query. Класс сам создает запрос для запрошенной страницы. Так что если пользователь захотел просмотреть посты по определенной категории из архива, WP создаст запрос и вытянет все посты по определенной категории, а Loop покажет их.

Но это самый простой пример основного запроса. Передав в новый экземпляр класса WP_Query массив с параметрами, мы можем расширить этот функционал и фильтровать результат. В примере ниже показано, как это сделать:

Выглядит немного сложнее, не так ли? Однако если рассмотреть код поближе, это не так. В новом объекте класса WP_Query хранится массив аргументов, который влияет на то, какие данные мы вытянем из БД. В кодексе можно найти полный список параметров, сгруппированных в 17 категорий. К примеру, там есть параметры автора, категорий, один параметр поиска (s), параметры пользовательских полей и т.д. (чуть позже мы вернемся к параметрам WP_Query).

После создания объекта $query нам доступны все его методы и свойства. Методы have_posts проверяет, остались ли еще посты для вывода, а the_post передвигает Loop к следующему посту и обновляет глобальную переменную $post.

При создании пользовательского запроса всегда необходимо вне цикла Loop вызывать wp_reset_postdata(). Эта функция восстанавливает глобальную переменную $post после выполнения пользовательского запроса. Это крайне важно, т.к. любой новый запрос перезаписывает переменную $post. Из кодекса:

«Обратите внимание: Если в своем запросе вы используете the_post(), после запроса вам понадобится запустить метод wp_reset_postdata(), чтобы Template Tags снова использовали основной запрос текущего поста.»

Вернемся к аргументам запроса.

Аргументы WP_Query

Мы уже сказали, что в классе WP_Query хранится массив параметров, с помощью которых разработчики могут выбирать, что вытянуть из БД.

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

author

author_name

author__in

author__not_in

Если нужно получить все посты от автора по имени carlo, необходимо прописать следующий запрос:

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

cat

category_name

category__and

category__in

category__not_in

Если нужно получит все посты по категории webdesign, необходимо просто задать аргумент category_name, как показано ниже:

Запрос ниже ищет все посты по двум категориям, запятая здесь заменяет OR:

Также можно искать посты, принадлежащие к обеим категориям webdesign и webdev, для этого необходимо поставит знак + между ними, он равносилен AND:

Также можно передать массив с ID:

И так далее с тегами и таксономиями, ключевыми словами для поиска, постами, страницами и типами постов. В кодексе можно найти более детальное рассмотрение аргументов запроса. Как уже было сказано выше, мы можем задать больше одного аргумента и вытянуть, к примеру, все посты по заданной категории определенного автора:

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

Мета-запросы WordPress

В кодексе говорится, что при работе с мета-зпросами WP_Query использует класс WP_Meta_Query. Данный класс впервые появился в WordPress 3.2 и строит SQL код запросов на основе пользовательских полей. Чтобы построить запрос на основе всего одного пользовательского поля, нам потребуется один или более аргументов:

meta_key

meta_value

meta_value_num

meta_compare

Допустим, пользовательский тип постов называется accommodation. Давайте присвоим каждому accommodation пользовательское поле city, в котором будет храниться географическое местоположение. С помощью мета-запроса мы можем вытянуть из БД все посты по типу accommodation с определенным городом. Для этого нужно всего лишь передать нужные аргументы в запрос, как показано ниже:

После создания аргументов можно строить запрос, как мы уже это делали раньше:

Вставьте код выше в файл шаблона и вы получите архив всех постов с типом accommodations, написанных в городе Freiburg.
Это был пример с одним пользовательским полем. А что, если нам нужно построить запрос на основе нескольких пользовательских полей?

meta_query аргумент

Для такого запроса в классе WP_Meta_Query есть параметр meta_query. Это массив массивов:

Параметр meta_query – это двумерный массив, чьи ячейки являются отдельными мета-запросами со следующими аргументами:

Если задать более одного пользовательского поля, нам потребуется назначить элементу meta_query аргумент relation. После всего этого мы можем построить более продвинутый запрос. Начнем с создания аргументов и нового объекта WP_Query:

Здесь в аргументе meta_query хранится два массива с мета-запросами, а третий параметр устанавливает связь между запросами. Запрос просеивает таблицу wp_posts на все посты типа accommodation с пользовательскими полями city = «Paris» и type = «room».

Скопируйте код выше в файл шаблона archive-accommodation.php. При запросе с данными параметрами WP произведен поиск в таблице wp_posts, а Loop покажет доступные результаты.

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

Фильтр pre_get_posts

Экшен pre_get_posts запускается после создания объекта $query, но перед его выполнением. Чтобы модифицировать запрос, нам придется прикрепить свою колбэк функцию pre_get_posts.

В одном из предыдущим примеров мы вытягивали все посты заданного автора с категорией webdesign. А в примере ниже мы будем передавать те же аргументы в объект $query, однако на этот раз мы не будем это делать в файле шаблона, а используем основной файл плагина (или файл темы functions.php). Напишем следующий блок кода:

Объект $query передается в колбэк функцию не по значению, а по ссылке, что означает, что любые изменения в запросе напрямую влияют на объект $query. В кодексе говорится:

«С помощью экшена pre_get_posts разработчики могут обращаться к объекту $query по ссылке (любые изменения в $query напрямую влияют на оригинальный объект – возвращать значение не обязательно).»

Так как мы манипулируем исходным объектом $query, нужно смотреть, с каким запросом мы работаем. Метод is_main_query проверяет, является ли объект $query основным запросом. В кодексе также говорится, что фильтр pre_get_posts может повлиять как на панель администратора, так и на front-end страницы. Поэтому крайне рекомендуется проверять запрашиваемую страницу с помощью условного тега is_admin.

Экшен pre_get_posts хорошо задокументирован. Вам стоит прочитать о нем в кодексе, где вы найдете подробное описание и различные примеры использования.

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

От теории к коду: создаем поисковую систему

Нам потребуется:

Создать структуру данных.

Зарегистрировать пользовательские переменные запроса.

Получить значения переменных и использовать их для создания пользовательского запроса.

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

Создаем структуру данных

Цель постов пользовательского типа в том, чтобы добавить контент, который нельзя логически включить в посты блога или статические страницы. Пользовательские типы постов особенно подходят для показа событий, товаров, книг, фильмов, каталогов предметов и т.д. Мы создадим архив объявлений о недвижимости по следующей структуре:

Пользовательский тип поста: accommodation;

Пользовательская таксономия: typology (мотель, дом, отель, и т.д.)

Пользовательские поля: _sm_accommodation_type (весь дом, личная комната, общая комната)

Пользовательское поле: _sm_accommodation_city

Другие пользовательские поля

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

Страница редактирования поста типа accommodation, справа показаны все пользовательские мета боксы и поля. На рисунке видно, как будет выглядеть сраница Edit Accommodation с тремя пользовательскими мета боксами, в которых хранится таксономия Typology и несколько зарегистрированных полей.

Наша цель не анализировать архитектуру данных WP, это за нас уже сделали пользователи сайта Smashing Magazine Daniel, Brian, Kevin, Josh и Justin. Если вам нужно освежить знания, можете прочесть их статьи и вернуться к моей. После создания архитектуры данных необходимо зарегистрировать переменные запроса.

Регистрация переменных запроса

В предыдущей части мы узнали, что переменные запроса это пара key=value после знака вопроса в строке URL. Однако перед тем, как мы сможем обработать эти пары в нашем скрипте, необходимо их зарегистрировать в плагине или файле functions.php. Нам понадобятся всего две переменные, с помощью которых мы сможем выполнять запрос на основе значений соответствующий пользовательских полей:

Вот и все! Мы добавили два дополнительных параметра в запрос к БД. Теперь необходимо написать URL типа: //example.com/?type=XXX&city=YYY

Манипуляция запросом

Добавьте новый кусок кода в наш скрипт:

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

Далее функция проверяет доступность ранее зарегистрированных переменных запроса. Проверка осуществляется при помощи функции get_query_var, которая вытягивает публичные переменные запроса из HTTP запроса (более подробно о get_query_var прочтите в кодексе). Если хоть одна переменная существует, колбэк функция создает по массиву на каждый мета-запрос и помещает их в многомерный массив $meta_query.

Если доступно хотя бы два мета-запроса, то к $meta_query добавляется аргумент relation со значением «AND». После этого метод set сохраняет $query для его последующего исполнения.

Вам не нужно беспокоиться о безопасности введенных данных, так как за нас это делают WP_Query и WP_Meta_Query (можете посмотреть исходный код WP_Query и WP_Meta_Query).

Создание формы поиска

Данные из формы отправляются методом GET, а значит, атрибуты полей формы name и value отправляются как переменные в URL (т.е. переменные запроса). Поэтому мы зададим атрибутам name полей формы те же значения, что и в ранее зарегистрированных переменных запроса (city и type), а значения самих полей могут быть назначены программно с помощью данных из БД или ручного ввода пользователя.

Сперва мы создадим шорткод, с помощью которого админы смогут подключить форму поиска к постам и страницам сайта. Наш шорткод подсоединяется к экшену init:

Далее мы создадим колбэк функцию, которая будет выводить HTML код формы с тремя полями: пользовательской таксономией и двумя пользовательскими полями.

$args – массив атрибутов шорткода. Внутрь функции мы добавим:

Мы создали запрос, который вытягивает все посты типа accommodation с заданным кастомным полем _sm_accommodation_city (подчеркивание перед именем означает скрытое пользовательское поле).

Цикл Loop не покажет посты типа accommodation, но добавить элементы соответствующего значения пользовательского поля в массив $cities. По условию дубликаты будут пропускаться. Если нет ни одного поста типа accommodation, функция прекращается; в противном случае элементы массива сортируются и используются для вывода значений первой группы тегов option в выпадающем списке.

Все доступные города в выпадающем списке. Второе поле формы это выпадающий список кастомной таксономии typology. Значения второй группы тегов option выводятся с помощью get_terms. Код ниже генерирует поле select для таксономии typology:

get_terms возвращает массив всех терминов набора таксономии в качестве первого аргумента или в виде объекта WP_Error, если таксономия не существует. И цикл foreach выводит теги option выпадающего списка.

Варианты второго выпадающего списка. Осталось последнее поле select, которое будет относиться к пользовательскому полю type. Код:

В этот раз, как можно заметить, набор опций задан вручную. И теперь мы можем вывести форму:

Мы создали скрытое поле для публичной переменной запроса post_type. Когда пользователь отправляет форму, WP получает значение post_type и загружает файл шаблона archive.php или если есть archive-{post_type}.php. Если вы будете настраивать HTML структуру страницы с результатами, для такой формы понадобится специальный файл шаблона.

На изображении показана форма продвинутого поиска, которую мы создали в этой статье.

Поиск по любому тексту

Созданная нами форма позволяет пользователям задавать три фильтра из огромного числа заранее заданных опций. Теперь мы улучшим поисковую систему и добавим текстовое поле, чтобы пользователи могли искать посты типа accommodation по тексту. Сделать это можно с помощью аргумента запроса s. Давайте изменим форму:

Благодаря текстовому полю мы можем передать в WP_Query новую пару ключ/значение, где ключ это параметр s, а значение это текст, введенный пользователем или возвращаемое значение функции get_search_query() (более подробно в кодексе).

Более расширенная поисковая форма. Последнее замечание: в предыдущем примере мы видели, как WP загружает файл шаблона архива для показа результата запроса. Это происходит потому, что мы не задали аргумент поиска. Когда в строке запроса есть параметр s, WP автоматически загружает файл шаблона страницы поиска, как показано на последнем изображении ниже.

Заключение

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

Автор: Carlo Daniele

Источник: //www.smashingmagazine.com/

Редакция: Команда webformyself.

Метки:

Похожие статьи:

Комментарии Вконтакте:

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