WordPress как на ладони
Недорогой хостинг для сайтов на WordPress: wordpress.jino.ru Получай пассивный доход от сайта

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

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

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

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

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

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

  • $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', '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_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 не ломая пагинацию.

Примеры

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

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

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

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

Когда пользователи вашего сайта ищут что-либо, часто в результатах поиска могут попадаться постоянные страницы, которые в принципе совсем не нужны в результатах поиска, и которые можно исключить из поиска насовсем. Используйте хук 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' );
	}
}

#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', [ '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 );
	}

	// Выводим 50 записей если это архив типа записи 'movie'
	if( $query->is_post_type_archive('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://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
		)

)

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

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

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

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

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

Использование не найдено.
22 коммента
Вопросы 3 Все
    Войти