pre_get_posts
Позволяет изменить запрос 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 не ломая пагинацию.
Примеры
#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' ] ); } }
#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' ); } }
#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' ); } }
#4 Пример объекта WP_Query
Для того, чтобы быстро разобраться как и что использовать, ниже пример объекта WP_Query (global $wp_query), который передается в хук по ссылке (&$query):
#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 | Введена. |
Где вызывается хук
do_action_ref_array( 'pre_get_posts', array( &$this ) );
Где используется хук в WordPress
add_action( 'pre_get_posts', '_resolve_template_for_new_post' );
remove_filter( 'pre_get_posts', '_resolve_template_for_new_post' );