Фильтрация по произвольному полю в произвольном типе записей на странице администратора
Я использовал Advanced Custom Fields для создания произвольных полей с названием соревнования, ответами и т.д. Я создал произвольный тип записи для соревнований, как показано на изображении, и использовал functions.php WordPress для создания колонок из значений моих произвольных полей.
Я пытаюсь добавить выпадающий список "Фильтровать по" с различными названиями/метками соревнований, как показано ниже, но я могу найти только решения с использованием таксономий, которые я предпочел бы не использовать если возможно, так как для всего остального я использовал только произвольные поля.
Возможно ли создать пользовательский выпадающий список "Фильтровать по" используя только произвольные поля?
Для отображения результатов фильтрации используйте следующий код:
add_filter( 'parse_query', 'prefix_parse_filter' );
function prefix_parse_filter($query) {
global $pagenow;
$current_page = isset( $_GET['post_type'] ) ? $_GET['post_type'] : '';
if ( is_admin() &&
'competition' == $current_page &&
'edit.php' == $pagenow &&
isset( $_GET['competition-name'] ) &&
$_GET['competition-name'] != '' ) {
$competition_name = $_GET['competition-name'];
$query->query_vars['meta_key'] = 'competition_name';
$query->query_vars['meta_value'] = $competition_name;
$query->query_vars['meta_compare'] = '=';
}
}
Измените meta_key и meta_value по мере необходимости. В данном примере я использовал "competition_name" в качестве meta_key и "competition-name" в качестве имени выпадающего списка.

Действие restrict_manage_posts запускает функцию add_extra_tablenav()
, которая позволяет добавить дополнительные выпадающие списки в нужную таблицу списков.
В примере ниже мы сначала проверяем, что Тип записи корректен, затем получаем все значения из БД, сохраненные по ключу competition_name
в таблице postmeta
(вам нужно изменить имя ключа по необходимости). Запрос довольно простой: он проверяет, опубликован ли Конкурс, берет только уникальные значения (чтобы избежать дублирования в выпадающем списке) и сортирует их по алфавиту.
Далее мы проверяем наличие результатов (нет смысла выводить пустой выпадающий список), формируем опции (включая вариант по умолчанию для отображения всех) и выводим сам список.
Как упомянуто в комментарии, на этом история не заканчивается: вам понадобится логика, чтобы таблица списков отображала только нужные результаты при активном фильтре. Но это уже тема для отдельного вопроса, если потребуется помощь. Подсказка — посмотрите файлы /wp-admin/includes/class-wp-posts-list-table.php
и его родителя .../wp-class-list-table.php
.
/**
* Добавляет дополнительные выпадающие списки в таблицы списков
*
* @param обязательный string $post_type Отображаемый тип записи
*/
add_action('restrict_manage_posts', 'add_extra_tablenav');
function add_extra_tablenav($post_type){
global $wpdb;
/** Проверяем, что это нужный тип записи */
if($post_type !== 'competition')
return;
/** Получаем результаты из БД */
$query = $wpdb->prepare('
SELECT DISTINCT pm.meta_value FROM %1$s pm
LEFT JOIN %2$s p ON p.ID = pm.post_id
WHERE pm.meta_key = "%3$s"
AND p.post_status = "%4$s"
AND p.post_type = "%5$s"
ORDER BY "%6$s"',
$wpdb->postmeta,
$wpdb->posts,
'competition_name', // Ваш мета-ключ — измените по необходимости
'publish', // Статус записи — измените по необходимости
$post_type,
'competition_name'
);
$results = $wpdb->get_col($query);
/** Проверяем, есть ли что выводить */
if(empty($results))
return;
// Получаем выбранную опцию, если она есть
if (isset( $_GET['competition-name'] ) && $_GET['competition-name'] != '') {
$selectedName = $_GET['competition-name'];
} else {
$selectedName = -1;
}
/** Формируем все опции для вывода */
$options[] = sprintf('<option value="-1">%1$s</option>', __('Все конкурсы', 'your-text-domain'));
foreach($results as $result) :
if ($result == $selectedName) {
$options[] = sprintf('<option value="%1$s" selected>%2$s</option>', esc_attr($result), $result);
} else {
$options[] = sprintf('<option value="%1$s">%2$s</option>', esc_attr($result), $result);
}
endforeach;
/** Выводим выпадающий список */
echo '<select class="" id="competition-name" name="competition-name">';
echo join("\n", $options);
echo '</select>';
}

При использовании этого кода я получаю ошибку Notice: wpdb::prepare was called incorrectly. The query does not contain the correct number of placeholders (6) for the number of arguments passed (5). Please see Debugging in WordPress for more information. (This message was added in version 4.8.3.) in /[...]/wp-includes/functions.php on line 4773

Если это не работает для кого-то, моим решением было добавление столбца, по которому я пытался фильтровать, в список сортируемых столбцов для моего пользовательского типа записи.
// Делаем пользовательские столбцы типа записи сортируемыми
function cpt_custom_columns_sortable( $columns ) {
// Добавляем наши столбцы в массив $columns
$columns['item_number'] = 'item_number';
$columns['coat_school'] = 'coat_school';
return $columns;
} add_filter( 'manage_edit-your-custom-post-type-slug_sortable_columns', 'cpt_custom_columns_sortable' );

Замените запрос ниже, чтобы исправить ошибку wpdb::prepare:
$query = $wpdb->prepare('
SELECT DISTINCT pm.meta_value FROM %1$s pm
LEFT JOIN %2$s p ON p.ID = pm.post_id
WHERE pm.meta_key = "%3$s"
AND p.post_status = "%4$s"
AND p.post_type = "%5$s"
ORDER BY "%3$s"',
$wpdb->postmeta,
$wpdb->posts,
'competition_name', // Ваш мета-ключ - измените при необходимости
'publish', // Статус записи - измените при необходимости
$post_type,
'competition_name' // необходимо указать второй раз для определения "%3$s" в ORDER BY
);

Я столкнулся с такой же проблемой, когда пытался фильтровать записи произвольного типа по произвольным полям.
Но я решил эту проблему, выполнив следующие шаги:
Изменил название произвольного поля с writer
на _writer
Затем обновил следующий код внутри функции обратного вызова
хука parse_query
, чтобы добавить meta_query для произвольного поля:
$query->set( 'meta_query', array(
array(
'key' => '_writer',
'compare' => '=',
'value' => $_GET['_writer'],
'type' => 'numeric',
)
) );
Это решение сработало в моем случае.
Документация https://developer.wordpress.org/reference/hooks/pre_get_posts/
Полезные ссылки https://developer.wordpress.org/reference/hooks/pre_get_posts/#comment-2571
https://stackoverflow.com/questions/47869905/how-can-i-filter-records-in-custom-post-type-list-in-admin-based-on-user-id-that
