WordPress. Фильтр записей по произвольным полям. Часть 2 из 3
02.10.2019
Теги: CMS • Web-разработка • WordPress • Запрос • КаталогТоваров • МетаДанные • Плагин • Форма • ШаблонСайта
Хорошо, у нас есть возможность добавления фильтров и мы можем устанавливать значения этих фильтров. Теперь добавим форму фильтра на страницу архива записей типа product
. Эта страница доступна по адресу /product/
сразу после регистрации нового типа записи. Если это не так — нужно в панели управления перейти на страницу настроек постоянных ссылок и просто нажать кнопку «Сохранить изменения».
Добавим в код плагина функцию, которая будет выводить форму фильтров:
/* * Функция выводит форму с фильтрами для товаров каталога */ function tokmakov_catalog_filters() { $filters = get_option('tokmakov_catalog_filters'); if (empty($filters)) { return; } /* * $filters = array( * 'size' => array( * 'text' = 'Размер', * 'value' => array( * 'small' => 'Маленький', * 'medium' => 'Средний', * 'large' => 'Большой' * ) * ), * 'color' => array( * 'text' = 'Цвет', * 'value' => array( * 'red' => 'Красный', * 'blue' => 'Синий', * 'green' => 'Зеленый' * ) * ), * ); */ $action = get_post_type_archive_link('product'); ?> <form action="<?= $action; ?>" method="get" id="tokmakov-catalog-filters"> <h3>Фильтры</h3> <?php foreach ($filters as $slug => $filter): ?> <fieldset> <legend><?= $filter['text']; ?></legend> <?php foreach ($filter['value'] as $value => $text): ?> <?php $checked = false; if (isset($_GET['filter'][$slug][$value])) { $checked = true; } ?> <input type="checkbox" name="filter[<?= $slug; ?>][<?= $value; ?>]" value="1"<?php checked($checked); ?> /> <?= $text ?> <?php endforeach; ?> </fieldset> <?php endforeach; ?> <input type="submit" name="apply_filter" value="Применить" /> </form> <?php }
Вызов этой функции нужно вставить в шаблон темы. Можно использовать archive.php
или создать отдельный шаблон для товаров — archive-product.php
(шаблон архива должен быть archive-{posttype}.php
). При использовании шаблона archive.php
потребуется дополнительная проверка, что мы находимся на странице архива записей типа product
:
if (is_post_type_archive('product')) { tokmakov_catalog_filters(); }
Так что второй вариант предпочтительнее:
<?php /** * Шаблон для показа списка всех товаров каталога, файл archive-product.php */ /* * Подключаем шапку сайта */ get_header(); ?> <div id="content"> <div class="container"> <div class="row"> <main class="col-md-9"> <h1>Все товары</h1> <p><?php the_archive_description(); ?></p> <?php tokmakov_catalog_filters(); ?> <?php get_template_part('parts/list', 'product'); ?> </main> <aside class="col-md-3"> <?php get_sidebar(); ?> </aside> </div> </div> </div> <?php /* * Подключаем подвал сайта */ get_footer(); ?>
Для оформления формы подключим css-файл frontend-style.css
/* * Подключаем файл стилей для публичной части сайта, чтобы оформить форму фильтров */ add_action('wp_enqueue_scripts', function () { wp_enqueue_style( 'tokmakov-catalog', plugin_dir_url(__FILE__) . 'frontend-style.css' ); });
#tokmakov-catalog-filters {
background: #eee;
padding: 20px 10px 10px 10px;
margin: 2em 0 1em 0;
border: 1px solid #ddd;
position: relative;
}
#tokmakov-catalog-filters h3 {
font-size: 1.2em;
position: absolute;
top: -12px;
margin: 0;
background: #fff;
padding: 2px 15px;
border: 1px solid #ddd;
border-radius: 15px;
display: inline-block;
}
#tokmakov-catalog-filters fieldset {
border: 1px solid #ddd;
padding: 5px 10px 10px 10px;
margin-bottom: 10px;
background: #fff;
}
#tokmakov-catalog-filters fieldset:last-child {
margin-bottom: 0;
}
#tokmakov-catalog-filters fieldset legend {
border: 1px solid #ddd;
padding: 0 10px;
border-radius: 12px;
background: #fff;
width: auto;
min-width: 70px;
font-size: 1em;
margin-bottom: 0;
}
#tokmakov-catalog-filters fieldset input[type="checkbox"] {
position: relative;
top: 2px;
}
Массив $_GET
, который надо обработать, выглядит так:
Array ( [filter] => Array ( [size] => Array ( [small] => 1 [large] => 1 ) [color] => Array ( [white] => 1 [black] => 1 ) ) [apply_filter] => Применить )
/* * Изменяем основной запрос к базе данных */ add_action('pre_get_posts', function ($query) { // только в публичной части сайта if (is_admin()) { return; } // нас интересует только главный запрос if (!$query->is_main_query()) { return; } // нам нужна страница архива записей... if (!$query->is_post_type_archive) { return; } // ...только одного типа — product if ($query->query['post_type'] != 'product') { return; } // если данные формы не отправлены — то и фильтровать не надо if (empty($_GET['filter'])) { return; } $filters = get_option('tokmakov_catalog_filters'); if (empty($filters)) { return; } /* $meta_query => array( 'relation' => 'AND', array( array( 'key' => 'size', 'value' => array('small', 'large'), 'compare' => 'IN', ) ), array( array( 'key' => 'color', 'value' => array('white', 'black'), 'compare' => 'IN', ) ), ) */ $meta_query = []; foreach ($filters as $slug => $filter) { $temp = []; foreach ($filter['value'] as $value => $text) { if (isset($_GET['filter'][$slug][$value])) { $temp[] = $value; } } if (!empty($temp)) { $meta_query[] = [ 'key' => '_tokmakov_catalog_filter_'.$slug, 'value' => $temp, 'compare' => 'IN' ]; } } if (!empty($meta_query)) { $meta_query['relation'] = 'AND'; $query->set('meta_query', $meta_query); } });
Основным параметром для работы с мета-данными в WP_Query
является meta_query
. Он получает записи по ключам и значениям произвольных полей. В нашем случае meta_query
может быть:
array( 'relation' => 'AND', array( 'relation' => 'OR', array( 'key' => 'size', 'value' => 'small', ), array( 'key' => 'size', 'value' => 'large', ), ), array( 'relation' => 'OR', array( 'key' => 'color', 'value' => 'white', ), array( 'key' => 'color', 'value' => 'black', ), ), )
array( 'relation' => 'AND', array( array( 'key' => 'size', 'value' => array('small', 'large'), 'compare' => 'IN', ) ), array( array( 'key' => 'color', 'value' => array('white', 'black'), 'compare' => 'IN', ) ), )
Мы использовали второй вариант и в итоге SQL-запрос будет таким:
SELECT SQL_CALC_FOUND_ROWS `wp_posts`.`ID` FROM `wp_posts` INNER JOIN `wp_postmeta` ON (`wp_posts`.`ID` = `wp_postmeta`.`post_id`) INNER JOIN `wp_postmeta` AS `mt1` ON (`wp_posts`.`ID` = `mt1`.`post_id`) WHERE 1 = 1 AND ( (`wp_postmeta`.`meta_key` = '_tokmakov_catalog_filter_size' AND `wp_postmeta`.`meta_value` IN ('small','large')) AND (`mt1`.`meta_key` = '_tokmakov_catalog_filter_color' AND `mt1`.`meta_value` IN ('white','black')) ) AND `wp_posts`.`post_type` = 'product' AND `wp_posts`.`post_status` = 'publish' GROUP BY `wp_posts`.`ID` ORDER BY `wp_posts`.`post_date` DESC LIMIT 0, 5
- WordPress. Фильтр записей по произвольным полям. Часть 3 из 3
- WordPress. Фильтр записей по произвольным полям. Часть 1 из 3
- WordPress. Добавляем мета-теги. Часть 3 из 3
- WordPress. Метабоксы. Часть 2 из 2
- WordPress. Добавляем мета-теги. Часть 2 из 3
- WordPress. Обработка POST-запросов. Часть 1
- WordPress. Защита страницы авторизации
Поиск: CMS • Web-разработка • WordPress • Каталог товаров • Мета данные • Форма • Шаблон сайта • Запрос • SQL • Плагин