Как переопределить изменения запроса The Events Calendar?

Вопрос или проблема

Я реализую некоторые специальные параметры поиска, которые должны исключать все, что не является типом 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

Чтобы дать немного больше деталей, я пытаюсь реализовать выпадающий список, который позволяет пользователям указывать то, что я называю типом поиска, который включает:

  1. Все – поведение поиска по умолчанию
  2. Блог-посты – только тип post
  3. Классы – посты типа tribe_events в категории classes
  4. Клубные события – посты типа 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
);

Применение

В вашей ситуации, чтобы полностью реализовать фильтрацию по типу записей и исключить типы, добавленные плагином, необходимо рассмотреть следующее:

  1. Использование правильных фильтров и хуков: Несмотря на удаление действий The Events Calendar, убедитесь, что ваши изменения корректно влияют на объект WP_Query. Используйте метод set для установки параметров в объекте WP_Query.

  2. Внедрение suppress_filters: Как было предложено, применение параметра suppress_filters = true может помочь в предотвращении вмешательства других фильтров WordPress, что позволит сохранить ваш запрос в неизменном виде:

    $query->set('suppress_filters', true);
  3. Адаптация к плагинам: Возможно, стоит рассмотреть адаптацию функций вашего кода к поведению The Events Calendar, особенно если модификации плагина важны для других частей сайта. Создайте интерфейсы и классы, которые могут управлять различными состояниями запросов.

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

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

Капча загружается...