Сышышь ты, выходи сюда,
поговорим !

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

Опубликовано: 01.09.2018

Событие срабатывает перед каждым запросом 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 ) )

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

... 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; ...
rss