WordPress. Фильтр записей по произвольным полям. Часть 1 из 3
30.09.2019
Теги: CMS • Web-разработка • WordPress • КаталогТоваров • МетаБокс • МетаДанные • Плагин • Таксономия
Давайте напишем плагин, который позволит фильтровать пользовательский тип записи по произвольным полям. За основу возьмем плагин «Каталог товаров», чтобы не начинать с полного нуля. Добавим для этого плагина страницу настроек в панели управления, где можно будет создавать фильтры для товаров. А для страницы редактирования записи типа product
создадим метабокс, позволяющий задать значения фильтров.
<?php /* Plugin Name: Каталог товаров Plugin URI: https://tokmakov.msk.ru Description: Позволяет создать на сайте простой каталог товаров с категориями и фильтами. Version: 1.0 Author: Евгений Токмаков Author URI: https://tokmakov.msk.ru */ register_activation_hook(__FILE__, function() { // проверяем права пользователя на установку плагинов if (!current_user_can('activate_plugins')) { return; } }); register_deactivation_hook(__FILE__, function() { // проверяем права пользователя на деактивацию плагинов if (!current_user_can('deactivate_plugins')) { return; } });
/* * Регистрируем пользовательский тип записи product */ add_action('init', function () { $labels = [ 'name' => 'Товары', 'menu_name' => 'Товары', 'singular_name' => 'Товар', 'add_new' => 'Добавить товар', 'add_new_item' => 'Добавить новый товар', 'edit_item' => 'Редактировать товар', 'new_item' => 'Новый товар', 'all_items' => 'Все товары', 'view_item' => 'Посмотреть товар', 'search_items' => 'Найти товары', 'not_found' => 'Ничего не найдено', 'not_found_in_trash' => 'В корзине не найдено' ]; $args = [ 'labels' => $labels, 'description' => 'Коллекции женской, мужской и детской обуви на все времена года.', 'public' => true, 'publicly_queryable' => true, 'show_ui' => true, 'show_in_menu' => true, 'query_var' => true, 'capability_type' => 'post', 'has_archive' => true, 'rewrite' => true, 'hierarchical' => false, 'menu_position' => null, 'supports' => [ 'title', 'editor', 'author', 'thumbnail', 'excerpt', 'custom-fields' ], 'taxonomies' => ['group'], ]; register_post_type('product', $args); });
/* * Регистрируем иерархическую таксономию (разделы каталога) */ add_action('init', function () { $labels = array( 'name' => 'Категории', 'singular_name' => 'Категория', 'menu_name' => 'Категории' , 'all_items' => 'Все категории', 'edit_item' => 'Редактировать категорию', 'view_item' => 'Посмотреть категорию', 'update_item' => 'Сохранить категорию', 'add_new_item' => 'Добавить новую категорию', 'parent_item' => 'Родительская категория', 'search_items' => 'Поиск по категориям', 'back_to_items' => 'Назад на страницу категорий', 'most_used' => 'Популярные категории', ); $args = array( 'labels' => $labels, 'show_admin_column' => true, 'hierarchical' => true, ); register_taxonomy('group', ['product'], $args); });
/* * Добавляем страницу настроек плагина */ add_action('admin_menu', function () { add_menu_page( // содержимое тега title этой страницы 'Плагин «Товары»', // название пункта меню для этой страницы 'Плагин «Товары»', // права доступа, чтобы был показан этот пункт меню 'manage_options', // уникальный идентификатор меню (страницы) 'tokmakov_catalog_settings', // функция выводит содержимое этой страницы function () { ?> <div class="wrap"> <h1>Настройки плагина «Каталог товаров»</h1> <form method="post" action="options.php"> <?php settings_fields('tokmakov_catalog_settings'); do_settings_sections('tokmakov_catalog_settings'); submit_button(); ?> </form> </div> <?php }, // иконка для страницы настроек плагина 'dashicons-admin-settings', // позиция страницы, в конце списка 90 ); });
Все фильтры будем хранить в опции tokmakov_catalog_filters
, в таблице БД wp_options
она будет сохраняться в виде сериализованного массива:
Array ( [size] => Array ( [text] => Размер [value] => Array ( [small] => маленький [medium] => средний [large] => большой ) ) [color] => Array ( [text] => Цвет [value] => Array ( [white] => белый [black] => черный ) ) )
/* * 1. Регистрируем опцию для плагина * 2. Добавляем секцию для этих опции * 3. Добавляем поля формы для опции */ add_action('admin_init', function () { /* * 1. Регистрируем опцию для плагина */ register_setting( // страница меню, куда будет добавлена эта опция 'tokmakov_catalog_settings', // уникальный идентификатор этой опции (slug) 'tokmakov_catalog_filters', /* * Эта функция нужна, чтобы обработать опцию перед сохранением. */ function ($option) { /* * $filters = array( * 'size' => array( * 'text' = 'Размер', * 'value' => array( * 'small' => 'маленький', * 'medium' => 'средний', * 'large' => 'большой' * ) * ), * 'color' => array( * 'text' = 'Цвет', * 'value' => array( * 'white' => 'белый', * 'black' => 'черный' * ) * ), * ); */ // сюда будем сохранять все данные $filters = []; foreach ($_POST['tokmakov_catalog_filter_name_slug'] as $i => $slug) { $slug = trim($slug); // проверяем slug названия фильтра (латиница) if (!preg_match('~^[a-z][_a-z0-9]*$~i', $slug)) { continue; } // slug названия фильтра должен быть уникальным if (isset($filters[$slug])) { continue; } // название фильтра (кириллица) if (!empty($_POST['tokmakov_catalog_filter_name_text'][$i])) { $text = $_POST['tokmakov_catalog_filter_name_text'][$i]; } else { $text = $slug; } $filters[$slug] = ['text' => $text]; foreach ($_POST['tokmakov_catalog_filter_value_slug'][$i] as $j => $value) { $value = trim($value); // проверяем slug значения фильтра (латиница) if (!preg_match('~^[a-z][_a-z0-9]*$~i', $value)) { continue; } // slug значения фильтра должен быть уникальным if (isset($filters[$slug]['value'][$value])) { continue; } // название значения фильтра (кириллица) if (!empty($_POST['tokmakov_catalog_filter_value_text'][$i][$j])) { $text = $_POST['tokmakov_catalog_filter_value_text'][$i][$j]; } else { $text = $value; } $filters[$slug]['value'][$value] = $text; } // нужно проверить, что у фильтра есть хотя бы одно значение if (empty($filters[$slug]['value'])) { array_pop($filters); } } /* * При редактировании могли быть удалены какие-то фильтры или их * значения. Значит, мы должны почистить таблицу БД `wp_postmeta` * от старых записей. */ global $wpdb; $query = "SELECT `".$wpdb->postmeta."`.`meta_id` AS `id`, SUBSTRING(`".$wpdb->postmeta."`.`meta_key`, 26) AS `slug`, CASE LEFT(`".$wpdb->postmeta."`.`meta_value`, 1) WHEN '—' THEN SUBSTRING(`".$wpdb->postmeta."`.`meta_value`, 2) ELSE `".$wpdb->postmeta."`.`meta_value` END AS `value` FROM `".$wpdb->postmeta."` WHERE LEFT(`".$wpdb->postmeta."`.`meta_key`, 25) = '_tokmakov_catalog_filter_'"; $result = $wpdb->get_results($query); foreach ($result as $item) { // если фильтр существует if (isset($filters[$item->slug])) { // и значение существует if (isset($filters[$item->slug]['value'][$item->value])) { // тогда ничего не делаем continue; } } // а иначе — удаляем запись $wpdb->delete( $wpdb->postmeta, ['meta_id' => $item->id] ); } return $filters; } ); /* * 2. Регистрируем новую секцию для опций */ add_settings_section( // уникальный идентификатор секции 'tokmakov_catalog_filters', // заголовок секции нам не нужен '', // функция выводит описание секции function () { ?> <h2>Фильтры для каталога</h2> <p> Создайте новые фильтры или отредактируйте уже существующие. Для каждого фильтра задаются два названия — кириллицей и латиницей. Аналогично задаются значения фильтров — кириллицей и латиницей. Названия фильтров латиницей должны быть уникальными и содержать только буквы, цифры и подчеркивание. Аналогично, значения фильтров латиницей должны быть уникальнвми внутри каждого фильтра. И содержать только буквы, цифры и подчеркивание. </p> <?php }, // страница, куда будет добавлена эта секция 'tokmakov_catalog_settings' ); /* * 3.1. Добавляем поле формы для редактирования первой опции */ add_settings_field( 'tokmakov_catalog_filters', // идентификатор поля формы 'Фильтры для каталога', // заголовок поля формы function () { // выводит html-код поля формы $filters = get_option('tokmakov_catalog_filters'); /* * $filters = array( * 'size' => array( * 'text' = 'Размер', * 'value' => array( * 'small' => 'маленький', * 'medium' => 'средний', * 'large' => 'большой' * ) * ), * 'color' => array( * 'text' = 'Цвет', * 'value' => array( * 'white' => 'белый', * 'black' => 'черный' * ) * ), * ); */ ?> <div id="tokmakov-catalog-filters"> <?php $i = 0; ?> <?php if (!empty($filters)): ?> <h4>Существующие фильтры</h4> <?php foreach ($filters as $slug => $filter): ?> <fieldset> <legend>Фильтр</legend> <p>Название фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_name_slug[<?= $i; ?>]" value="<?= esc_attr($slug); ?>" /> <input type="text" name="tokmakov_catalog_filter_name_text[<?= $i; ?>]" value="<?= esc_attr($filter['text']); ?>" /> </p> <p>Значения фильтра (латиница + кириллица)</p> <?php foreach ($filter['value'] as $key => $value): ?> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[<?= $i; ?>][]" value="<?= esc_attr($key); ?>" /> <input type="text" name="tokmakov_catalog_filter_value_text[<?= $i; ?>][]" value="<?= esc_attr($value); ?>" /> </p> <?php endforeach; ?> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[<?= $i; ?>][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[<?= $i; ?>][]" value="" placeholder="Значение фильтра (кир)" /> </p> </fieldset> <?php $i++; ?> <?php endforeach; ?> <?php endif; ?> <h4>Добавить новый фильтр</h4> <fieldset> <legend>Фильтр</legend> <p>Название фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_name_slug[<?= $i; ?>]" value="" placeholder="Название фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_name_text[<?= $i; ?>]" value="" placeholder="Название фильтра (кир)" /> </p> <p>Значения фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[<?= $i; ?>][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[<?= $i; ?>][]" value="" placeholder="Значение фильтра (кир)" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[<?= $i; ?>][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[<?= $i; ?>][]" value="" placeholder="Значение фильтра (кир)" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[<?= $i; ?>][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[<?= $i; ?>][]" value="" placeholder="Значение фильтра (кир)" /> </p> </fieldset> </div> <?php }, 'tokmakov_catalog_settings', // страница меню 'tokmakov_catalog_filters' // идентификатор секции ); });
Форма для добавления и редактирования фильтров имеет вид:
<div id="tokmakov-catalog-filters"> <h4>Существующие фильтры</h4> <fieldset> <legend>Фильтр</legend> <p>Название фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_name_slug[0]" value="size" /> <input type="text" name="tokmakov_catalog_filter_name_text[0]" value="Размер" /> </p> <p>Значения фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[0][]" value="small" /> <input type="text" name="tokmakov_catalog_filter_value_text[0][]" value="маленький" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[0][]" value="medium" /> <input type="text" name="tokmakov_catalog_filter_value_text[0][]" value="средний" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[0][]" value="large" /> <input type="text" name="tokmakov_catalog_filter_value_text[0][]" value="большой" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[0][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[0][]" value="" placeholder="Значение фильтра (кир)" /> </p> </fieldset> <fieldset> <legend>Фильтр</legend> <p>Название фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_name_slug[1]" value="color" /> <input type="text" name="tokmakov_catalog_filter_name_text[1]" value="Цвет" /> </p> <p>Значения фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[1][]" value="white" /> <input type="text" name="tokmakov_catalog_filter_value_text[1][]" value="белый" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[1][]" value="black" /> <input type="text" name="tokmakov_catalog_filter_value_text[1][]" value="черный" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[1][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[1][]" value="" placeholder="Значение фильтра (кир)" /> </p> </fieldset> <h4>Добавить новый фильтр</h4> <fieldset> <legend>Фильтр</legend> <p>Название фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_name_slug[2]" value="" placeholder="Название фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_name_text[2]" value="" placeholder="Название фильтра (кир)" /> </p> <p>Значения фильтра (латиница + кириллица)</p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[2][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[2][]" value="" placeholder="Значение фильтра (кир)" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[2][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[2][]" value="" placeholder="Значение фильтра (кир)" /> </p> <p> <input type="text" name="tokmakov_catalog_filter_value_slug[2][]" value="" placeholder="Значение фильтра (лат)" /> <input type="text" name="tokmakov_catalog_filter_value_text[2][]" value="" placeholder="Значение фильтра (кир)" /> </p> </fieldset> </div>
Массив $_POST
, который надо обработать и записать в опцию, имеет вид:
Array ( [tokmakov_catalog_filter_name_slug] => Array ( [0] => size [1] => color [2] => ) [tokmakov_catalog_filter_name_text] => Array ( [0] => Размер [1] => Цвет [2] => ) [tokmakov_catalog_filter_value_slug] => Array ( [0] => Array ( [0] => small [1] => medium [2] => large [3] => ) [1] => Array ( [0] => white [1] => black [2] => ) [2] => Array ( [0] => [1] => [2] => ) ) [tokmakov_catalog_filter_value_text] => Array ( [0] => Array ( [0] => маленький [1] => средний [2] => большой [3] => ) [1] => Array ( [0] => белый [1] => черный [2] => ) [2] => Array ( [0] => [1] => [2] => ) ) [submit] => Сохранить изменения )
/* * Подключаем файл стилей для панели управления, чтобы оформить форму фильтров */ add_action('admin_enqueue_scripts', function () { wp_enqueue_style( 'tokmakov-catalog', plugin_dir_url(__FILE__) . 'backend-style.css' ); });
#tokmakov-catalog-filters {
}
#tokmakov-catalog-filters h4 {
margin: 7px 0;
}
#tokmakov-catalog-filters fieldset {
border: 1px solid #ddd;
padding: 10px;
margin-bottom: 10px;
background: #fff;
}
#tokmakov-catalog-filters fieldset legend {
border: 1px solid #ddd;
padding: 1px 10px 2px 10px;
border-radius: 12px;
background: #fff;
}
/* * Добавляем метабокс на страницу редактирования товара */ add_action('add_meta_boxes', function () { add_meta_box( 'tokmakov-catalog-filters', 'Значения фильтров', function ($post) { $filters = get_option('tokmakov_catalog_filters'); if (empty($filters)) { return 'Создайте фильтры на странице настроек плагина'; } ?> <?php foreach ($filters as $slug => $filter): ?> <?php // значения текущего фильтра для этого товара $values = get_post_meta( $post->ID, '_tokmakov_catalog_filter_' . $slug ); ?> <fieldset> <legend><?= $filter['text']; ?></legend> <?php foreach ($filter['value'] as $value => $text): ?> <?php $checked = in_array($value, $values); ?> <input type="checkbox" name="tokmakov_catalog_filter_<?= $slug; ?>[]" value="<?= $value; ?>" <?php checked($checked); ?> /> <?= $text; ?> <?php endforeach; ?> </fieldset> <?php endforeach; ?> <input type="hidden" name="tokmakov_catalog_filters" value="1" /> <?php }, ['product'], 'normal', 'high' ); });
/* * Сохраняем данные из формы добавления-редактирования товара */ add_action('save_post', function ($post_id) { // проверяем права пользователя if(!current_user_can('edit_post', $post_id)) { return; } // если это автосохранение, то ничего не делаем if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) { return; } // тип записи должен быть product $post = get_post($post_id); if ($post->post_type != 'product') { return; } // только при отправке формы, которую мы изменили if (!isset($_POST['tokmakov_catalog_filters'])) { return; } // фильтры еще не созданы, ничего не делаем $filters = get_option('tokmakov_catalog_filters'); if (empty($filters)) { return; } /* * Сначала в качестве значений всех фильтров записываем специальное * значение, означающее «пустое значение», например «—small» или * «—red». Потом обрабатываем POST-данные и записываем реальные * значения фильтров для товара, например «small» или «red». */ foreach ($filters as $slug => $filter) { foreach ($filter['value'] as $value => $text) { $meta = get_post_meta( $post_id, '_tokmakov_catalog_filter_' . $slug, false ); if (!in_array($value, $meta) && !in_array('—'.$value, $meta)) { /* * Если такого значения фильтра еще нет, то создаем и записываем * «пустое значение», которое чуть ниже (возможно) перезапишем */ add_post_meta( $post_id, '_tokmakov_catalog_filter_' . $slug, '—' . $value ); } else { /* * Если такое значение фильтра есть, то запишем «пустое значение», * которое чуть ниже (возможно) перезапишем */ update_post_meta( $post_id, '_tokmakov_catalog_filter_' . $slug, '—' . $value, $value ); } } } foreach ($filters as $slug => $filter) { foreach ($filter['value'] as $value => $text) { if (isset($_POST['tokmakov_catalog_filter'][$slug][$value])) { update_post_meta( $post_id, '_tokmakov_catalog_filter_' . $slug, $value, '—'.$value ); } } } });
Массив $_POST
, который нужно обработать при сохранении значений фильтров для товара:
Array ( /*...*/ [tokmakov_catalog_filter] => Array ( [size] => Array ( [small] => 1 [large] => 1 ) [color] => Array ( [white] => 1 [black] => 1 ) ) [tokmakov_catalog_filters] => 1 /*...*/ )
- WordPress. Фильтр записей по произвольным полям. Часть 3 из 3
- WordPress. Фильтр записей по произвольным полям. Часть 2 из 3
- WordPress. Добавляем мета-теги. Часть 3 из 3
- WordPress. Метабоксы. Часть 2 из 2
- WordPress. Метабоксы. Часть 1 из 2
- WordPress. Произвольные типы записей
- WordPress. Пользовательские таксономии
Поиск: CMS • Web-разработка • WordPress • Каталог товаров • Мета бокс • Мета данные • Плагин • Таксономия