Поиск по произвольным полям с помощью класса WP_Query

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

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




Данные произвольных полей WordPress хранятся в таблице wp_postmeta и представляют собой пары ключ – значение на основе двух столбцов: meta_key и meta_value соответственно. meta_value имеет тип LONGTEXT, что подразумевает хранение данных строкового типа. Это значит, что нет никакой разницы, какого типа вы создаете поле в Magic Fields. Чекбокс это или селект, текстовое поле или список радио кнопок, данные все равно будут храниться в виде строк. Этими замечаниями я пытаюсь ответить на вопрос о том, как осуществлять поиск по полям разных типов. В предыдущих заметках я рассматривал select списки и приводил пример поиска именно для них. Но это не значит, что для поиска, например, по текстовому полю будет применяться другая логика. При любых обстоятельствах, вы будете передавать серверной части строковое значение и поиск будет производиться именно сравнением строк.


Структура таблицы wp_postmeta

Предлагаю рассмотреть небольшой пример, в основе которое будет лежать максимально простая html форма c тремя полями: text, checkbox и select. Для реализации логики составления поискового запроса будет использован класс WP_Query как наиболее гибкий инструмент. В целом, ничто не запрещает писать запросы вручную. В некоторых случаях это единственный способ.

Пример поисковой формы:

Исходный код:

<form method="post" class="form-horizontal form-search">
<fieldset>
	<legend>Поиск</legend>
	<div class="control-group">
		<label class="control-label" for="item-name">Наименование</label>
		<div class="controls">
			<input type="text" class="input-xlarge" id="item-name" name="item-name">
		</div>
	</div>
	<div class="control-group">
		<label class="control-label" for="item-type">Тип документа</label>
		<div class="controls">
			<select name="item-type" id="item-type">
				<option value="Счет">Счет</option>
				<option value="Акт">Акт</option>
				<option value="Договора">Договор</option>
			</select>
		</div>
	</div>
	<div class="control-group">
		<div class="controls">
			<label class="checkbox"><input id="item-published" type="checkbox" name="item-published" value="1">Опубликован</label>
		</div>
	</div>
	<div class="form-actions">
		<input class="btn btn-primary" type="submit" value="Отправить" />
	</div>
</fieldset>
</form>

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

SELECT *
FROM `wp_posts` AS `posts`
JOIN `wp_postmeta` AS `meta` ON (
	`meta`.`post_id` = `posts`.ID AND (
		(`meta`.`meta_key` = 'item-name' AND `meta`.`meta_value` = 'Тест')
		AND (`meta`.`meta_key` = 'item-type' AND `meta`.`meta_value` = 'Счет')
		AND (`meta`.`meta_key` = 'item-type' AND `meta`.`meta_value` = '1'))
	)
WHERE 1

WHERE 1 – не есть правильно, так как необходимо выбирать только опубликованные посты, с прошедшей датой публикации и т.п. Чтобы не писать все эти условия вручную, лучше всего использовать упомянутый класс WP_Query, который возьмет эти обязанности на себя.

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

<?php

$params = array(
    'item-name',
    'item-type',
    'item-published'
);

$wp_query = new WP_Query();

$x_array = array();

foreach ($params as $param) {
    if (array_key_exists($param, $_POST) && !empty($_POST[$param])) {
        $x_array[] = array(
        	'key' => $param,
        	'value' => $_POST[$param],
        	'compare' => '='
        );
    }
}

if (!empty($x_array)) {
	$wp_query->query(array(
		'meta_query' => $x_array
	));
}

?>

Массив $params содержит имена доступных для поиска параметров. Далее, в цикле, происходит проверка наличия каждого параметра в массиве $_POST. Таким образом происходит подготовка критериев выборки для поискового запроса. Ключ compare не является обязательным и по умолчанию равен «=», и приводится здесь исключительно для наглядности. Значением ключа может быть любой тип сравнения, доступный в MySQL, в том числе LIKE, который, скорее всего, пригодится при организации поиска для текстовых полей.

Если скрипту, реализующему данную логику, передан хотя бы один параметр для поиска, то есть массив $x_array не пуст, вызывается метод WP_Query::query(), которому передаем массив $x_array в качестве значения ключа meta_query.

Далее выводим посты привычным способом:

<?php

while ($this->wp_query->have_posts()) {
    $this->wp_query->the_post();

    // ...
}

?>

Класс WP_Query позволяет формировать достаточно сложные запросы, с чередующимися AND и OR, с различными типами сравнения, группировками и прочим. Самое главное, что он автоматически подставляет требуемые критерии выборки: дата публикации, статус публикации и т.п. Так же лишает нужды переписывать громоздкие запросы, если вы добавляете новый критерий в выборку.

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

В завершении еще раз напомню, что для механизма произвольных полей, созданных стандартными средствами WP или через плагины, такие как Magic Fields, не имеет никакого значения, какого типа будет поля у поисковой формы. Поиск будет производиться по данным строкового типа и только.

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

  1. Всеволод

    RSS подписки на сайте не вижу, или плохо искал?

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

  2. Евгений

    Добрый день, Олег!
    Я не программист, поэтому прошу помощи.
    Подскажите, пожалуйста, как написать поиск по критерию:
    значение ТЕХТ поля post_title должно совпадать со значением страницы с записью ТЕХТ и вывод этого единственного результата поиска. Спасибо.

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

  3. Добрый день Олег! такая проблема: города отображаются но не выбираются и если ввожу значение, то не чего по нему не находит…, код аналогичен Вашему только убрал третье поле

    • >>> города отображаются но не выбираются

      Это как?

      • они выбираются, но не отображаются в поиске, т.е. не участвуют в нем…

        • Видимо, где-то допущена ошибка в вашем коде. Как бы там ни было, я вряд ли смогу вам сказать что-то конкретное не видя код.

          • Сергей

            Найти

            Город

            Краснодар
            Москва
            Абинск

            $param,
            ‘value’ => $_POST[$param],
            ‘compare’ => ‘=’
            );
            }
            }

            if (!empty($x_array)) {
            $wp_query->query(array(
            ‘meta_query’ => $x_array
            ));
            }

            ?>
            и еще такой вопрос: можно вывести при помощи запроса id из бд и реализовать поиск с этой функцией ?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *