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()работает только для основного запроса.

Gmazzap
источник
Ни в одном из моих поисков по сайту или онлайн-поиска я не сталкивался с этим. Скрытый драгоценный камень, который работает. Каждое решение использует 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' );
    }
}
Nicolai
источник
Проверим это завтра. Спасибо за ваше подробное объяснение.
Питер Гусен
Мое удовольствие как всегда @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
Ну, это действительно не удивительно, хотя. Как и любой другой фильтр, он просто фильтрует вещи. Он не знает контекста, когда его вызывают. Это может даже не вызываться изнутри правильного цикла. Нет способа сказать это.
Отто