the_content и is_main_query


15

Я фильтрую содержимое с помощью the_contentфильтра. Все работает отлично, за исключением того, что мои изменения применяются и к пользовательским запросам. Мои изменения также отображаются на боковой панели, если виджет использует пользовательский запрос

Чтобы противостоять этому, я использую is_main_query()только целевой запрос, но он не работает. Изменения просто все еще применяются ко всем запросам. Что забавно, все другие условные проверки работают is_single()и is_category()работают, если я нацеливаюсь на определенные страницы, за исключением того, что все изменения влияют на любой другой пользовательский запрос на этой странице, независимо от того, использую я is_main_query()или нет

Я что-то здесь упускаю? Как применить мои изменения к основному запросу только с помощью the_contentфильтра

add_filter('the_content', 'custom_content');

function custom_content($content){

    if(is_main_query()){ // << THIS IS NOT WORKING
        // My custom content that I add to the_content()    
    }
    return $content;
}

Ответы:


11

Если честно, функция in_the_loop()- это то, что вы ищете:

add_filter( 'the_content', 'custom_content' );

function custom_content( $content ) {
    if ( in_the_loop() ) {
        // My custom content that I add to the_content()    
    }
    return $content;
}

Что in_the_loopнужно сделать, это проверить, является ли глобальный $wp_queryобъект (то есть основной объект запроса) текущей записи -1 < $current_post < $post_count.

Это происходит, когда основной запрос зацикливается, потому что перед началом цикла текущая запись равна -1, а после окончания цикла текущая запись снова сбрасывается в -1.

Таким образом, если in_the_loop()true, это означает, что основной объект запроса является зацикленным, что вам и нужно в этом случае (и это тот же результат добавления действия loop_startи удаления на него loop_end, как писал ответ @ialocin; на самом деле это работает по той же причине и получил мой +1).

Преимущество подхода @ ialocin состоит в том, что вы хотите нацеливать объект запроса, отличный от основного, потому что он in_the_loop()работает только для основного запроса.


Ни в одном из моих поисков по сайту или онлайн-поиска я не сталкивался с этим. Скрытый драгоценный камень, который работает. Каждое решение использует is_main_query, действительно думайте, что никто не проверил это полностью. Спасибо за ваш вклад, очень признателен
Питер Гусен

1
@PieterGoosen Рад, что это работает. Это очень старая функция, пришедшая прямо из времен, когда is_main_queryничего не было.
gmazzap

Видите ли, это то, где я пропустил это, я не старый таймер :-), присоединился к Wordpress в 3.3.
Питер Гусен

2
@GM не могли бы вы добавить это в свой ответ. Это полезная информация для других, которые могут наткнуться на этот ответ. Большинство людей, как и я, не читают комментарии :-)
Питер Гусен

1
@PieterGoosen сделано :)
gmazzap

7

Это всего лишь дополнение к ответу @ Otto. Просто чтобы сделать его немного лучше понятным. В основном то, что говорит @Otto, вы должны изменить логику, что означает: если вы можете надежно определить основной запрос, то вы можете добавить - и удалить - свою зацепку в the_contentфильтр.

Например, основной запрос может быть надежно распознан при pre_get_postsдействии, поэтому это будет работать:

function wpse162747_the_content_filter_callback( $content ) {
    return $content . 'with something appended';
}

add_action( 'pre_get_posts', 'wpse162747_pre_get_posts_callback' );
function wpse162747_pre_get_posts_callback( $query ) {
    if ( $query->is_main_query() ) {
        add_filter( 'the_content', 'wpse162747_the_content_filter_callback' );
    }
}

Поскольку вы должны удалить фильтр, когда он больше не нужен, я думаю, что loop_endдействие должно быть хорошим местом для этого, и в качестве его аналога мы можем использовать loop_start. Который будет выглядеть так:

add_action( 'loop_start', 'wpse162747_loop_start_callback' );
function wpse162747_loop_start_callback( $query ) {
    if ( $query->is_main_query() ) {
        add_filter( 'the_content', 'wpse162747_the_content_filter_callback' );
    }
}

add_action( 'loop_end', 'wpse162747_loop_end_callback' );
function wpse162747_loop_end_callback( $query ) {
    if ( $query->is_main_query() ) {
        remove_filter( 'the_content', 'wpse162747_the_content_filter_callback' );
    }
}

Проверим это завтра. Спасибо за ваше подробное объяснение.
Питер Гусен

Мое удовольствие как всегда @PieterGoosen Не спешите, но сделайте это, потому что я не - по крайней мере, недостаточно.
Николай

1
Что если шорткод используется в the_content () и шорткод запускает другой запрос, который вызывает the_content (), сбрасывает текущий объект post и цикл продолжается? Все фильтры будут применяться. Довольно хитроумно, не сохраняемый звонком in_the_loop () ... Вот почему я предлагаю всегда удалять уникальные фильтры, как только они это сделали, к чему @Nicolai
Jonas Lundman

5

Вы используете is_main_query()неправильно. Глобальная функция is_main_query () возвращает true, если глобальная переменная $ wp_query не была переопределена.

Вероятно, нет 100% надежного способа определить из фильтра the_content, является ли текущий цикл, в котором вы находитесь, основным запросом или нет. Фильтр контента просто фильтрует контент. У него нет никакой возможности узнать, для какого цикла он используется.

Вместо этого вы должны добавить свой фильтр, когда вам это нужно, а затем удалить его, когда вы этого не делаете.


Это на самом деле разочарование, что нет простых средств для нацеливания основного запроса с помощью the_contentфильтра
Pieter Goosen

Ну, это действительно не удивительно, хотя. Как и любой другой фильтр, он просто фильтрует вещи. Он не знает контекста, когда его вызывают. Это может даже не вызываться изнутри правильного цикла. Нет способа сказать это.
Отто
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.