WordPress как на ладони
wordpress jino

pre_get_posts хук-событие . WP 2.0

Событие срабатывает перед каждым запросом WP_Query. До того, как был сделан запрос в базу. Используется для изменения запроса.

Событие pre_get_posts дает возможность изменять объект $wp_query, потому что объект передается в хук по ссылке (&), это означает, что любые действия над $query внутри функции буду влиять на основной объект $wp_query. Для этого хука не надо возвращать никаких данных.

ВАЖНО! pre_get_posts срабатывает для каждого запроса WP_Query: основной запрос, дополнительный запрос, запрос в админ-панели, запросы в виджетах и т.д.

Поэтому нужно убедитесь, что вы изменяете именно нужны вам запрос, для этого перед изменением запроса используйте всевозможные Условные теги, чтобы точно ограничить изменение (см. примеры).

Часто используемые функции внутри хука pre_get_posts:

Использование

add_action( 'pre_get_posts', 'action_function_name_11' );
function action_function_name_11( $query ) {
	// Действия...
}

Параметры

$query(объект)
Объект WP_Query.

Заметки

Аргумент передается по ссылке

Объект $query предается по ссылке, поэтому нет необходимости определять глобальную переменную. Любые изменения $query внутри функции влияют сразу на оригинальный объект.

Запросы постоянных страниц

pre_get_posts нельзя использовать для изменения запросов связанных с постоянными страницами, потому что 'is_page', 'is_singular', 'pagename' и другие параметры (зависимые к ЧПУ) уже установлены в объекте. Рекомендуется использовать new WP_Query в шаблоне постоянной страницы, чтобы изменить запрос.

Определение нужного запроса

При использовании pre_get_posts, нужно точно определить, в какой конкретно запрос вы вносите изменения. Полезный метод для этого: $query->is_main_query() - он поможет вам убедиться, что вносимые изменения будут влиять только на основные запросы. Используйте его в связке с условными тегами. Чтобы изменять запрос только для страниц, которые вам нужны.

Например, мы хотим изменить запрос на странице категорий и не делаем проверку is_category(), тогда наши изменения будут влиять на формирования запроса в админ-панели, на других страницах сайта и где угодно еще. Поэтому четко определяйте, для какого запроса вы вносите изменения через действие-хук pre_get_posts.

Использование в админ-панели

Этот фильтр можно также использовать для изменения запросов в админ-панели. В таких случаях, убедитесь что внесенные изменения будут работать на странице вывода записей. Например, проверка $query->is_main_query() и is_post_type_archive('movie') (изменяем запрос для лицевой части для записей типа movie), изменит также запрос на страницы edit.php?post_type=movie. Чтобы этого не произошло, нужно использовать еще проверку ! is_admin().

Внимание! Условные теги

pre_get_posts срабатывает до того, как объект WP_Query полностью определится. Поэтому некоторые условные теги, опирающиеся на данные WP_Query еще не работают. Например, is_front_page() не работает, тогда как is_home() будет работать. Поэтому вам лучше работать с данными объекта напрямую, например: $query->is_search.

Весь список свойств которые можно использовать вместо условного тега:

$query->is_404
$query->is_admin
$query->is_archive
$query->is_attachment
$query->is_author
$query->is_category
$query->is_comments_popup
$query->is_comment_feed
$query->is_date
$query->is_day
$query->is_feed
$query->is_home
$query->is_month
$query->is_page
$query->is_paged
$query->is_posts_page
$query->is_post_type_archive
$query->is_preview
$query->is_robots
$query->is_search
$query->is_single
$query->is_singular
$query->is_tag
$query->is_tax
$query->is_time
$query->is_trackback
$query->is_year

// функции
$query->is_front_page()
$query->is_main_query()

Все остальные условные теги нужно заменить проверкой или методом класса. Например, is_front_page(), нужно заменить на такую проверку:

if( $query->is_home || ($query->get('page_id') == get_option('page_on_front')) ){
	// это front_page
}

или такой метод:

if( $query->is_front_page() ){
	// это front_page
}

is_main_query() не рекомендуется использовать внутри этого хука. Нужно отдать предпочтение методу $query->is_main_query().

Отступы и пагинация

Использование аргумента offset в любом запросе может сломать пагинацию. Поэтому, если вы используете offset, то вам нужно изменить запрос для всех страниц пагинации, опираясь на первоначальный отступ (offset). Подробнее об этом я писал в этой статье: Как использовать параметр offset не ломая пагинацию.

Примеры

#1 Исключение категорий на главной

Этот пример показывает как убрать посты из указанных категорий из вывода на главной странице блога. Например, у нас есть 2 категории с ID 1 и 1347, которые нам не нужно показывать на главной. Чтобы исключить эти категории из запроса, используйте такой код в плагине или в теме:

function exclude_category( $query ) {
	if ( $query->is_front_page() && $query->is_main_query() ) {
		$query->set( 'cat', '-1,-1347' );
	}
}
add_action( 'pre_get_posts', 'exclude_category' );

#2 Исключение постоянных страниц из результатов поиска

Когда пользователи вашего сайта ищут что-либо, часто в результатах поиска могут попадаться постоянные страницы, которые в принципе совсем не нужны в результатах поиска, и которые можно исключить из поиска насовсем. Используйте хук pre_get_posts, чтобы исключить из результатов поиска постоянных страницы:

function search_filter($query) {
  if ( ! is_admin() && $query->is_main_query() ) {
	if ($query->is_search) {
	  $query->set('post_type', 'post');
	}
  }
}

add_action( 'pre_get_posts', 'search_filter' );

#3 Включение произвольного типа записей в результаты поиска

Включать в поиск произвольный тип записи или нет, устанавливается при регистрации типа записи, в аргументах функции register_post_type(): аргумент public=true добавляет в результат поиска произвольный тип записи.

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

add_action('pre_get_posts', 'get_posts_search_filter');
function get_posts_search_filter( $query ){
	if ( ! is_admin() && $query->is_main_query() && $query->is_search ) {
		$query->set('post_type', array('post', 'movie') );
	}
}

#4 Изменение количества выводимых на странице постов

В WordPress есть глобальная настройка, которая учитывает сколько записей показывать на странице - posts_per_page. Лучше всего изменять этот параметр до основного запроса, в целях экономии ресурсов, чтобы не делать повторных запрос. Так, мы можем использовать хук-действие pre_get_posts, чтобы изменить количество выводимых записей на странице.

Этот пример, показывает как перезаписать параметр posts_per_page для страницы архивов произвольного типа записи movie:

add_action('pre_get_posts', 'hwl_home_pagesize', 1 );
function hwl_home_pagesize( $query ) {
	// Выходим, если это админ-панель или не основной запрос.
	if( is_admin() || ! $query->is_main_query() )
		return;

	if( is_home() ){
		// Выводим только 1 пост на главной странице
		$query->set( 'posts_per_page', 1 );
	}

	if( $query->is_post_type_archive('movie') ){
		// Выводим 50 записей если это архив типа записи 'movie'
		$query->set( 'posts_per_page', 50 );
	}
}

Пример объекта WP_Query

Для того, чтобы быстро разобраться как и что использовать, ниже пример объекта WP_Query (global $wp_query), который передается в хук по ссылке (&$query):

WP_Query Object
(
	[query_vars] => Array
		(
			[page] => 0
			[pagename] => s
			[error] => 
			[m] => 
			[p] => 0
			[post_parent] => 
			[subpost] => 
			[subpost_id] => 
			[attachment] => 
			[attachment_id] => 0
			[name] => s
			[static] => 
			[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
			[comments_popup] => 
			[meta_key] => 
			[meta_value] => 
			[preview] => 
			[s] => 
			[sentence] => 
			[fields] => 
			[menu_order] => 
			[category__in] => Array
				(
				)

			[category__not_in] => Array
				(
				)

			[category__and] => Array
				(
				)

			[post__in] => Array
				(
				)

			[post__not_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] => 
			[suppress_filters] => 
			[cache_results] => 
			[update_post_term_cache] => 1
			[update_post_meta_cache] => 1
			[post_type] => 
			[posts_per_page] => 10
			[nopaging] => 
			[comments_per_page] => 10
			[no_found_rows] => 
			[order] => DESC
		)

	[tax_query] => 
	[meta_query] => WP_Meta_Query Object
		(
			[queries] => Array
				(
				)

			[relation] => 
		)

	[date_query] => 
	[post_count] => 1
	[current_post] => -1
	[in_the_loop] => 
	[comment_count] => 0
	[current_comment] => -1
	[found_posts] => 1
	[max_num_pages] => 0
	[max_num_comment_pages] => 0
	[is_single] => 
	[is_preview] => 
	[is_page] => 1
	[is_archive] => 
	[is_date] => 
	[is_year] => 
	[is_month] => 
	[is_day] => 
	[is_time] => 
	[is_author] => 
	[is_category] => 
	[is_tag] => 
	[is_tax] => 
	[is_search] => 
	[is_feed] => 
	[is_comment_feed] => 
	[is_trackback] => 
	[is_home] => 
	[is_404] => 
	[is_comments_popup] => 
	[is_paged] => 
	[is_admin] => 
	[is_attachment] => 
	[is_singular] => 1
	[is_robots] => 
	[is_posts_page] => 
	[is_post_type_archive] => 
	[query_vars_hash] => c248b13e251e8fba33892e0bd7a5bd98
	[query_vars_changed] => 
	[thumbnails_cached] => 
	[stopwords:WP_Query:private] => 
	[query] => Array
		(
			[page] => 
			[pagename] => s
		)

	[queried_object] => WP_Post Object
		(
			[ID] => 19
			[post_author] => 1
			[post_date] => 2010-04-01 20:09:20
			[post_date_gmt] => 2010-04-01 16:09:20
			[post_content] => 
			[post_title] => Страница для исполнения
			[post_excerpt] => 
			[post_status] => private
			[comment_status] => open
			[ping_status] => closed
			[post_password] => 
			[post_name] => s
			[to_ping] => 
			[pinged] => 
			[post_modified] => 2011-04-25 17:44:42
			[post_modified_gmt] => 2011-04-25 13:44:42
			[post_content_filtered] => 
			[post_parent] => 0
			[guid] => http://site.ru/s
			[menu_order] => 0
			[post_type] => page
			[post_mime_type] => 
			[comment_count] => 0
			[filter] => raw
		)

	[queried_object_id] => 19
	[request] => SELECT   wp_posts.* FROM wp_posts  WHERE 1=1  
				 AND (wp_posts.ID = '19') AND wp_posts.post_type = 'page'  
				 ORDER BY wp_posts.post_date DESC 
	[posts] => Array
		(
			[0] => WP_Post Object
				(
					[ID] => 19
					[post_author] => 1
					[post_date] => 2010-04-01 20:09:20
					[post_date_gmt] => 2010-04-01 16:09:20
					[post_content] => 
					[post_title] => Страница для исполнения
					[post_excerpt] => 
					[post_status] => private
					[comment_status] => open
					[ping_status] => closed
					[post_password] => 
					[post_name] => s
					[to_ping] => 
					[pinged] => 
					[post_modified] => 2011-04-25 17:44:42
					[post_modified_gmt] => 2011-04-25 13:44:42
					[post_content_filtered] => 
					[post_parent] => 0
					[guid] => http://site.ru/s
					[menu_order] => 0
					[post_type] => page
					[post_mime_type] => 
					[comment_count] => 0
					[filter] => raw
				)

		)

	[post] => WP_Post Object
		(
			[ID] => 19
			[post_author] => 1
			[post_date] => 2010-04-01 20:09:20
			[post_date_gmt] => 2010-04-01 16:09:20
			[post_content] => 
			[post_title] => Страница для исполнения
			[post_excerpt] => 
			[post_status] => private
			[comment_status] => open
			[ping_status] => closed
			[post_password] => 
			[post_name] => s
			[to_ping] => 
			[pinged] => 
			[post_modified] => 2011-04-25 17:44:42
			[post_modified_gmt] => 2011-04-25 13:44:42
			[post_content_filtered] => 
			[post_parent] => 0
			[guid] => http://site.ru/s
			[menu_order] => 0
			[post_type] => page
			[post_mime_type] => 
			[comment_count] => 0
			[filter] => raw
		)

)

Где используется хук

WP_Query::get_posts() остальные хуки:

Код хука-события pre_get_posts

Фрагмент из: wp-includes/class-wp-query.php VER 4.9.7
...
		global $wpdb;

		$this->parse_query();

		/**
		 * Fires after the query variable object is created, but before the actual query is run.
		 *
		 * Note: If using conditional tags, use the method versions within the passed instance
		 * (e.g. $this->is_main_query() instead of is_main_query()). This is because the functions
		 * like is_main_query() test against the global $wp_query instance, not the passed one.
		 *
		 * @since 2.0.0
		 *
		 * @param WP_Query $this The WP_Query instance (passed by reference).
		 */
		do_action_ref_array( 'pre_get_posts', array( &$this ) );

		// Shorthand.
		$q = &$this->query_vars;

		// Fill again in case pre_get_posts unset some vars.
		$q = $this->fill_query_vars($q);

		// Parse meta query
		$this->meta_query = new WP_Meta_Query();
		$this->meta_query->parse_query_vars( $q );

		// Set a flag if a pre_get_posts hook changed the query vars.
		$hash = md5( serialize( $this->query_vars ) );
		if ( $hash != $this->query_vars_hash ) {
			$this->query_vars_changed = true;
...
25 комментов
Вопросы 3 Все
  • kolshix345 cайт: paxtoy.com

    Изаю дополнительный поиск только по рубрикам - как в админке в разделе к примеру ТЕГИ, там поиск ищет не посты, а имена рубрик

    Сначала искал быстрое решение, в итоге пришлось этим пользоваться (JS не захотел)

    // Очистка из поисковой фразы пробелов - Зачем? При поиске по имени рубрики - нет результатов если в конце поисковой фразы пробел
    function search_filter($query) {
      if ( ! is_admin() && $query->is_main_query() ) {
    	if ($query->is_search) {
    		$search_text = trim(get_search_query());
    		$query->set('s', $search_text );
    	}
      }
    }
    
    add_action( 'pre_get_posts', 'search_filter' );
    Ответитьмесяц назад #
Здравствуйте, !     Войти . Зарегистрироваться