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

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

Позволяет изменить запрос WP_Query. Срабатывает перед запросом.

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

ВАЖНО! Это событие срабатывает для абсолютно каждого запроса WP_Query:

  • основной запрос
  • дополнительный запрос
  • запрос в админ-панели
  • запросы в виджетах
  • и т.д.

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

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

  • $query->is_main_query() — изменим только основной запрос WP.
  • is_admin() — изменим запросы только в админ панели.
  • get_queried_object() — данные текущего запроса (текущей страницы).

Использование get_queried_object() внутри этого хука, нужно претворять проверкой $query->is_author:

add_action( 'pre_get_posts', 'function_name' );
function function_name( $query ){

	if( $query->is_author )
		$qo = $query->queried_object();
	else
		$qo = get_queried_object();
}

Похоже что это баг при котором, для страницы автора слишком рано использовать get_queried_object() до обработки данных запроса. Именно при обработке устанавливается свойство $wp_query->query_vars['author'] на основе которого устанавливается объект юзера на странице юзера в функции get_queried_object(). А раз этих данных нет, то и текущий объект юзера получить не удастся.

Создал тикет по этому багу: https://core.trac.wordpress.org/ticket/51829

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

add_action( 'pre_get_posts', 'wp_kama_pre_get_posts_action' );

/**
 * Function for `pre_get_posts` action-hook.
 * 
 * @param WP_Query $query The WP_Query instance (passed by reference).
 *
 * @return void
 */
function wp_kama_pre_get_posts_action( $query ){

	// action...
}
$query(WP_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().

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

Это событие срабатывает до того, как объект 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_front_page() ){
	// это front_page
}

или на такую проверку:

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

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

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

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

Примеры

2

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

Включать в поиск произвольный тип записи или нет, устанавливается при регистрации типа записи, в аргументах функции 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', [ 'post', 'movie' ] );
	}
}

1

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

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

add_action( 'pre_get_posts', 'exclude_category_on_front_page' );

function exclude_category_on_front_page( $query ) {

	if ( $query->is_front_page() && $query->is_main_query() ) {

		$query->set( 'cat', '-1,-1347' );
	}
}

0

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

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

add_action( 'pre_get_posts', 'search_filter' );
function search_filter( $query ){

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

0

#4 Пример объекта 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://example.com/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://example.com/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://example.com/s
			[menu_order] => 0
			[post_type] => page
			[post_mime_type] =>
			[comment_count] => 0
			[filter] => raw
		)

)
-1

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

В 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 );
	}

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

Список изменений

С версии 2.0.0 Введена.

Где вызывается хук

WP_Query::get_posts()
pre_get_posts
wp-includes/class-wp-query.php 1881
do_action_ref_array( 'pre_get_posts', array( &$this ) );

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

wp-includes/block-template.php 16
add_action( 'pre_get_posts', '_resolve_template_for_new_post' );
wp-includes/block-template.php 342
remove_filter( 'pre_get_posts', '_resolve_template_for_new_post' );
19 комментариев
Полезные 1Вопросы 1 Все
    Войти