Вопрос или проблема
Я реализую некоторые специальные параметры поиска, которые должны исключать все, что не является типом post
. Календарь событий вмешивается и модифицирует запрос на лету.
Моя модификация запроса выглядит так:
$query->post_type
И я выполняю это до запроса:
remove_action(
'pre_get_posts',
[ 'Tribe__Events__Query', 'pre_get_posts' ],
50
);
remove_action(
'parse_query',
[ 'Tribe__Events__Query', 'parse_query' ],
50
);
…и восстанавливаю действия после выполнения для возврата нормальной работы.
Таким образом, с помощью этого отладочного кода:
add_filter('query', function($sql) {
global $wp_query;
if (isset($_GET['s']) && strpos($sql, $_GET['s']) !== false) {
debug($wp_query->post_type);
debug($wp_query->tax_query);
debug($sql);
}
return $sql;
});
…я вижу, что post_type в объекте запроса устанавливается правильно, и tax_query
находится в настройках по умолчанию (нет ничего из календаря событий). Но когда фильтр query
WordPress выполняется, фактический SQL, который запускается, выглядит так:
SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts
WHERE 1=1
AND (((wp_posts.post_title LIKE '%persian%') OR (wp_posts.post_excerpt LIKE '%persian%') OR (wp_posts.post_content LIKE '%persian%')))
AND (wp_posts.post_password = '')
AND wp_posts.post_type IN ('post', 'page', 'attachment', 'tribe_venue', 'tribe_events', 'tribe-ea-record', 'service', 'facility')
AND (wp_posts.post_status="publish" OR wp_posts.post_status="acf-disabled"
OR wp_posts.post_status="tribe-ea-success"
OR wp_posts.post_status="tribe-ea-failed"
OR wp_posts.post_status="tribe-ea-schedule"
OR wp_posts.post_status="tribe-ea-pending"
OR wp_posts.post_status="tribe-ea-draft")
ORDER BY wp_posts.post_title LIKE '%persian%'
DESC, wp_posts.post_date DESC LIMIT 0, 6
Изменение 1
Вот полный дамп объекта WP_Query
:
WP_Query::__set_state(array(
'query' =>
array (
's' => 'persian',
),
'query_vars' =>
array (
's' => 'persian',
'error' => '',
'm' => '',
'p' => 0,
'post_parent' => '',
'subpost' => '',
'subpost_id' => '',
'attachment' => '',
'attachment_id' => 0,
'name' => '',
'static' => '',
'pagename' => '',
'page_id' => 0,
'second' => '',
'minute' => '',
'hour' => '',
'day' => 0,
'monthnum' => 0,
'year' => 0,
'w' => 0,
'category_name' => '',
'tag' => '',
'cat' => '',
'tag_id' => '',
'author' => '',
'author_name' => '',
'feed' => '',
'tb' => '',
'paged' => 0,
'meta_key' => '',
'meta_value' => '',
'preview' => '',
'sentence' => '',
'title' => '',
'fields' => '',
'menu_order' => '',
'embed' => '',
'category__in' =>
array (
),
'category__not_in' =>
array (
),
'category__and' =>
array (
),
'post__in' =>
array (
),
'post__not_in' =>
array (
),
'post_name__in' =>
array (
),
'tag__in' =>
array (
),
'tag__not_in' =>
array (
),
'tag__and' =>
array (
),
'tag_slug__in' =>
array (
),
'tag_slug__and' =>
array (
),
'post_parent__in' =>
array (
),
'post_parent__not_in' =>
array (
),
'author__in' =>
array (
),
'author__not_in' =>
array (
),
'ignore_sticky_posts' => false,
'suppress_filters' => false,
'cache_results' => true,
'update_post_term_cache' => true,
'lazy_load_term_meta' => true,
'update_post_meta_cache' => true,
'post_type' => 'any',
'posts_per_page' => 6,
'nopaging' => false,
'comments_per_page' => '50',
'no_found_rows' => false,
'search_terms_count' => 1,
'search_terms' =>
array (
0 => 'persian',
),
'search_orderby_title' =>
array (
0 => 'wp_posts.post_title LIKE \'%persian%\'',
),
'order' => 'DESC',
),
'tax_query' =>
WP_Tax_Query::__set_state(array(
'queries' =>
array (
),
'relation' => 'AND',
'table_aliases' =>
array (
),
'queried_terms' =>
array (
),
'primary_table' => 'wp_posts',
'primary_id_column' => 'ID',
)),
'meta_query' =>
WP_Meta_Query::__set_state(array(
'queries' =>
array (
),
'relation' => NULL,
'meta_table' => NULL,
'meta_id_column' => NULL,
'primary_table' => NULL,
'primary_id_column' => NULL,
'table_aliases' =>
array (
),
'clauses' =>
array (
),
'has_or_relation' => false,
)),
'date_query' => false,
'post_count' => 0,
'current_post' => -1,
'in_the_loop' => false,
'comment_count' => 0,
'current_comment' => -1,
'found_posts' => 0,
'max_num_pages' => 0,
'max_num_comment_pages' => 0,
'is_single' => false,
'is_preview' => false,
'is_page' => false,
'is_archive' => false,
'is_date' => false,
'is_year' => false,
'is_month' => false,
'is_day' => false,
'is_time' => false,
'is_author' => false,
'is_category' => false,
'is_tag' => false,
'is_tax' => false,
'is_search' => true,
'is_feed' => false,
'is_comment_feed' => false,
'is_trackback' => false,
'is_home' => false,
'is_404' => false,
'is_embed' => false,
'is_paged' => false,
'is_admin' => false,
'is_attachment' => false,
'is_singular' => false,
'is_robots' => false,
'is_posts_page' => false,
'is_post_type_archive' => false,
'query_vars_hash' => '1b23d8a973f2ad41269c66f71f8365d4',
'query_vars_changed' => false,
'thumbnails_cached' => false,
'stopwords' =>
array (
0 => 'about',
1 => 'an',
2 => 'are',
3 => 'as',
4 => 'at',
5 => 'be',
6 => 'by',
7 => 'com',
8 => 'for',
9 => 'from',
10 => 'how',
11 => 'in',
12 => 'is',
13 => 'it',
14 => 'of',
15 => 'on',
16 => 'or',
17 => 'that',
18 => 'the',
19 => 'this',
20 => 'to',
21 => 'was',
22 => 'what',
23 => 'when',
24 => 'where',
25 => 'who',
26 => 'will',
27 => 'with',
28 => 'www',
),
'compat_fields' =>
array (
0 => 'query_vars_hash',
1 => 'query_vars_changed',
),
'compat_methods' =>
array (
0 => 'init_query_flags',
1 => 'parse_tax_query',
),
'tribe_is_event' => false,
'tribe_is_multi_posttype' => false,
'tribe_is_event_category' => false,
'tribe_is_event_venue' => false,
'tribe_is_event_organizer' => false,
'tribe_is_event_query' => false,
'tribe_is_past' => false,
'post_type' => 'post',
'request' => 'SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND (((wp_posts.post_title LIKE \'%persian%\') OR (wp_posts.post_excerpt LIKE \'%persian%\') OR (wp_posts.post_content LIKE \'%persian%\'))) AND (wp_posts.post_password = \'\') AND wp_posts.post_type IN (\'post\', \'page\', \'attachment\', \'tribe_venue\', \'tribe_events\', \'tribe-ea-record\', \'service\', \'facility\') AND (wp_posts.post_status = \'publish\' OR wp_posts.post_status = \'acf-disabled\' OR wp_posts.post_status = \'tribe-ea-success\' OR wp_posts.post_status = \'tribe-ea-failed\' OR wp_posts.post_status = \'tribe-ea-schedule\' OR wp_posts.post_status = \'tribe-ea-pending\' OR wp_posts.post_status = \'tribe-ea-draft\') ORDER BY wp_posts.post_title LIKE \'%persian%\' DESC, wp_posts.post_date DESC LIMIT 0, 6',
'posts' => NULL,
))
Отсюда я вижу, что дополнительные clauses tribe-* уже присутствуют. Не совсем уверен, откуда они берутся и как их убрать.
Изменение 2
Чтобы дать немного больше деталей, я пытаюсь реализовать выпадающий список, который позволяет пользователям указывать то, что я называю типом поиска, который включает:
- Все – поведение поиска по умолчанию
- Блог-посты – только тип
post
- Классы – посты типа
tribe_events
в категорииclasses
- Клубные события – посты типа
tribe_events
в категорииclub-events
Вот схема того, что у меня есть на данный момент:
В functions.php
add_action( 'pre_get_posts', function( WP_Query $query ) {
$searchType = isset($_GET['search_type']) ? $_GET['search_type'] : '';
SearchFilter\AbstractBase::filter_results_by_search_type($query, $searchType);
});
SearchFilter/AbstractBase.php
namespace SearchFilter;
use WP_Query;
use Timber;
abstract class AbstractBase {
/**
* Поддерживаемые типы поиска и конкретный класс, ответственный за модификацию запроса для каждого типа
*/
protected static $SUPPORTED_TYPES = [
'post' => 'wac\SearchFilter\PostFilter',
'tribe_events_category_classes' => 'wac\SearchFilter\ClassFilter',
'tribe_events_category_club_events' => 'wac\SearchFilter\ClubEventFilter',
];
/**
* Экземпляр SearchFilter\AbstractBase для получения запросов
*/
protected static $filteredSearchQuery;
/**
* Модификация $query для ограничения результатов поиска по указанному $type.
* Поддерживаемые типы перечислены в свойстве $SUPPORTED_TYPES.
*/
public static function filter_results_by_search_type(WP_Query $query, $type="") {
// воздействовать только на общие поисковые запросы
if ( ! is_admin() && $query->is_main_query() && $query->is_search() ) {
// выяснить, какой это тип поиска
if (isset(static::$SUPPORTED_TYPES[$type])) {
$class = static::$SUPPORTED_TYPES[$type];
$filter = new $class($query);
} else {
$filter = new DefaultFilter($query);
}
$filter->modify_query();
static::$filteredSearchQuery = $filter;
}
}
/**
* Конструктор
* @param WP_Query $query объект WP_Query для модификации
*/
protected function __construct(WP_Query $query) {
$this->query = $query;
}
/**
* Возвращает модифицированный запрос
*/
abstract public function modify_query();
public function get_posts() {
$this->override_tribe_hooks();
$posts = Timber::get_posts();
$this->restore_tribe_hooks();
return $posts;
}
protected function hide_recurring_events() {
$this->query->set( 'tribeHideRecurrence', true );
$this->query->set( 'eventDisplay', 'upcoming' );
$this->query->tribe_is_multi_posttype = true;
$this->query->set('meta_query', array(
'relation' => 'OR',
array(
'key' => '_EventStartDate',
'value' => date('Y-m-d h:i:s'),
'compare' => '>=',
'type' => 'DATE'
),
array(
'key' => '_EventEndDate',
'value' => date('Y-m-d h:i:s'),
'compare' => '>',
'type' => 'DATE'
),
array(
'relation' => 'AND',
array(
'key' => '_EventStartDate',
'compare' => 'NOT EXISTS',
'type' => 'DATE'
),
array(
'key' => '_EventEndDate',
'compare' => 'NOT EXISTS',
'type' => 'DATE'
)
)
));
}
/**
* Удаление стандартного хука The Events Calendar для запросов событий
*/
protected function override_tribe_hooks() {
remove_action(
'pre_get_posts',
[ 'Tribe__Events__Query', 'pre_get_posts' ],
static::TRIBE_ACTION_PRIORITY
);
remove_action(
'parse_query',
[ 'Tribe__Events__Query', 'parse_query' ],
static::TRIBE_ACTION_PRIORITY
);
}
protected function restore_tribe_hooks() {
add_action(
'pre_get_posts',
[ 'Tribe__Events__Query', 'pre_get_posts' ],
static::TRIBE_ACTION_PRIORITY
);
add_action(
'parse_query',
[ 'Tribe__Events__Query', 'parse_query' ],
static::TRIBE_ACTION_PRIORITY
);
}
SearchFilter/DefaultFilter.php
namespace SearchFilter;
use Timber;
class DefaultFilter extends AbstractBase {
public function modify_query() {
$this->hide_recurring_events();
}
public function get_posts() {
return Timber::get_posts();
}
}
SearchFilter/PostFilter.php
namespace SearchFilter;
use WP_Query;
class PostFilter extends AbstractBase {
public function modify_query() {
$this->query->query_vars->post_type = ['post'];
}
}
search.php
$data['posts'] = SearchFilter\AbstractBase::get_filtered_query()->get_posts();
// render posts
Кстати, Timber – это низкоуровневый плагин, который предоставляет более объектно-ориентированный интерфейс для get_posts
и подобных функций. Я много раз использовал его в проектах WordPress, и он никогда не создавал проблем, так что я не думаю, что проблема в нем.
Что я упускаю?
Мне удалось сделать нечто подобное с использованием параметра запроса supress_filters = true
.
Обратитесь к Codex для получения дополнительной информации о том, как это работает: https://codex.wordpress.org/Template_Tags/get_posts#Parameters
.
Ответ или решение
Вопрос о том, как переопределить изменения запроса плагина The Events Calendar, требует глубокого понимания работы WordPress, в частности, обработки запросов WP_Query и подключения сторонних плагинов. Давайте рассмотрим эту задачу через призму метода TEA (Теория, Пример, Применение).
Теория
WordPress использует объект WP_Query для извлечения данных из базы в зависимости от определенных критериев. The Events Calendar, являясь плагином для управления событиями, влияет на этот процесс, модифицируя запросы для своих нужд. Изменения в запросах могут вызвать конфликты, особенно если на сайте есть специфические требования к фильтрации и отображению контента.
The Events Calendar подключается к различным этапам выполнения запросов (например, действия pre_get_posts
и parse_query
) и добавляет свои модификации, что может мешать другим настройкам. В такой ситуации необходимо либо временно отключить эти модификации, либо адаптироваться к их работе.
Пример
В представленном коде показано, как удалить действия плагина с хука pre_get_posts
, чтобы предотвратить его вмешательство в пользовательские модификации:
remove_action(
'pre_get_posts',
[ 'Tribe__Events__Query', 'pre_get_posts' ],
50
);
remove_action(
'parse_query',
[ 'Tribe__Events__Query', 'parse_query' ],
50
);
Однако, это лишь часть решения. Необходимо также восстановить данные действия после выполнения ваших собственных модификаций, что позволить плагину продолжить работу без сбоев:
add_action(
'pre_get_posts',
[ 'Tribe__Events__Query', 'pre_get_posts' ],
50
);
add_action(
'parse_query',
[ 'Tribe__Events__Query', 'parse_query' ],
50
);
Применение
В вашей ситуации, чтобы полностью реализовать фильтрацию по типу записей и исключить типы, добавленные плагином, необходимо рассмотреть следующее:
-
Использование правильных фильтров и хуков: Несмотря на удаление действий The Events Calendar, убедитесь, что ваши изменения корректно влияют на объект WP_Query. Используйте метод
set
для установки параметров в объектеWP_Query
. -
Внедрение suppress_filters: Как было предложено, применение параметра
suppress_filters = true
может помочь в предотвращении вмешательства других фильтров WordPress, что позволит сохранить ваш запрос в неизменном виде:$query->set('suppress_filters', true);
-
Адаптация к плагинам: Возможно, стоит рассмотреть адаптацию функций вашего кода к поведению The Events Calendar, особенно если модификации плагина важны для других частей сайта. Создайте интерфейсы и классы, которые могут управлять различными состояниями запросов.
В результате корректного внедрения подобных приемов ваш сайт сможет эффективно отфильтровывать данные без нежелательных влияний со стороны других плагинов. Всегда тщательно тестируйте изменения, чтобы убедиться, что они не оказывают негативного влияния на другие функциональности сайта.