Вопрос или проблема
Я создаю шорткод, который выводит пользовательский цикл. Внутри цикла while
я внедряю определенное содержимое после определенного количества постов. Это делается путем проверки номера current_post
. Эта конкретная функция довольно длинная (и заполнена закомментированным кодом из тестирования).
Вот ссылка на мою текущую версию (в данный момент с ВСЕМИ постами на одной странице): https://gist.github.com/Garconis/c93ce89c3d378bfb7f5d8df97acbebc2
Это работает хорошо, когда показываются ВСЕ посты (posts_per_page => -1
), но как только я добавляю пагинацию (например, устанавливаю posts_per_page => 5
), внедренное содержимое начинает с нуля на каждой новой странице.
Как я могу обновить это, чтобы иметь возможность внедрять мое конкретное содержимое после X постов, не начиная счет заново с каждой новой страницы?
Основная часть того, что у меня есть:
<?php
// создаем шорткод для списка всех рекомендуемых маршрутов
add_shortcode( 'fs_burn_homepage_loop','fs_all_featured_itineraries_shortcode' );
function fs_all_featured_itineraries_shortcode( $atts ) {
ob_start();
// Запрос постов из базы данных.
$options = array(
// Аргументы для вашего запроса.
'post_type' => array('post', 'ajde_events'),
'posts_per_page' => 5,
'paged' => 1,
);
/* Чтобы включить работу плагина Easy Load More */
if ( get_query_var( 'paged' ) ) {
$options['paged'] = get_query_var( 'paged' );
}
elseif ( get_query_var( 'page' ) ) {
$options['paged'] = get_query_var( 'page' );
} else {
$options['paged'] = 1;
}
$query = new WP_Query( $options );
// Проверяем, что у нас есть результаты запроса
if ( $query->have_posts() ) {
// у нас есть посты, так что давайте их обернем
echo'<div id="fs-homepage-loop" class="fs-all-posts">';
// Начинаем цикл по результатам запроса.
while ( $query->have_posts() ) {
// Настраиваем данные поста.
$query->the_post();
echo '<article id="post-'. get_the_ID() .'" class="fs-post-article '. join( ' ', get_post_class() ) .'">
<div class="fs-post-content-wrapper">
<div class="fs-post-name">
<h2 class="entry-title"><a href="'. get_permalink(); .'">'. get_the_title() .'</a></h2>
</div>
</div>
</article>';
// внедряем содержимое после X постов
if( ($query->current_post == 3) ) {
echo '<article class="fs-post-article">
<div class="fs-post-content-wrapper">
содержимое после поста 4 здесь
</div>
</article>';
}
if( ($query->current_post == 9) ) {
echo '<article class="fs-post-article">
<div class="fs-post-content-wrapper">
содержимое после поста 10 здесь
</div>
</article>';
}
// закрываем цикл WHILE
}
wp_reset_postdata();
// закрываем обертку поста
echo '</div>';
// функция для плагина Easy Load More
load_more_button();
$myvariable = ob_get_clean();
return $myvariable;
// закрываем IF запроса
}
// Иначе не может найти НИКАКИХ постов, которые соответствуют
else {
echo '<div class="fs-no-posts-found"><h4>К сожалению, у нас нет историй, чтобы показать вам.</h4></div>';
// закрываем ELSE запроса */
}
}
Было бы так же просто как-то проверять счет current_post
И номер страницы пагинации? Поскольку я, по сути, знаю, сколько постов должно отображаться на странице…
Для справки, я только что нашел новый/другой способ достичь этой цели. Он не совсем такой же, но позволяет мне вставить новый тип поста после каждых X постов в моем оригинальном цикле.
Вот источник: https://www.gowp.com/blog/inserting-custom-posts-loop/
Метод был выполнен с помощью кода ниже, который, по сути, внедряет новый пользовательский тип поста в цикл, и, похоже, он работает независимо от пагинации.
add_action( 'wp', 'fs_only_do_on_homepage' );
function fs_only_do_on_homepage() {
if( is_front_page() ) {
/* изменяем запрос, чтобы внедрять рекламу после каждых X частот */
// https://www.gowp.com/blog/inserting-custom-posts-loop/
add_filter( 'the_posts', 'fs_insert_ads', 10, 2 );
function fs_insert_ads( $posts, $query ) {
// Мы не хотим, чтобы наш фильтр влиял на списки постов в админской панели
if ( is_admin() ) return $posts;
// Мы также хотим, чтобы наш фильтр влиял только на основной запрос
if ( ! is_main_query() ) return $posts;
// Устанавливаем переменную для частоты вставки, которую мы будем использовать в некоторых расчетах позже
$freq = 2;
/* Получаем рекламу и рассчитываем posts_per_page на лету,
* основываясь на значении из оригинального/основного запроса И нашей частоты
* это помогает гарантировать, что вставленные посты остаются синхронизированными независимо от настроек пагинации
*/
$args = array(
'post_type' => 'fs_ads',
'posts_per_page' => floor( $query->query_vars['posts_per_page'] / $freq ),
'paged' => ( $query->query_vars['paged'] ) ? $query->query_vars['paged'] : 1,
'orderby' => 'menu_order',
'order' => 'ASC',
'meta_key' => 'status',
'meta_value' => 'enable',
);
// вставляем рекламу в массив $posts, используя array_slice() ... и делаем некоторые расчеты, чтобы гарантировать правильную вставку
if ( $fs_ads = get_posts( $args ) ) {
$insert = -1;
foreach ( $fs_ads as $fs_ad ) {
$insert = $insert + ( $freq + 1 );
array_splice( $posts, $insert, 0, array( $fs_ad ) );
}
}
return $posts;
} // конец функции fs_insert_ads
} // конец проверки is_home
} // конец функции fs_only_do_on_homepage
Я теряю возможность сказать, какой элемент идет между какими постами, но все же достигаю своей первоначальной цели, пытаясь внедрить пользовательский тип поста в мой цикл после каждых X постов.
Ответ или решение
Чтобы решить вашу задачу по инъекции контента после определённого количества постов, независимо от пагинации, вы действительно можете использовать подход с использованием фильтров. Суть в том, что вместо того, чтобы проверять текущий пост в контексте вашей пользовательской петли (while
), вы можете использовать фильтр the_posts
, который позволяет модифицировать массив постов перед тем, как они выводятся на экран.
Ниже приведен полный пример кода, который позволяет вставить контент между постами во время выполнения основного запроса, независимо от пагинации:
add_action( 'wp', 'fs_only_do_on_homepage' );
function fs_only_do_on_homepage() {
if( is_front_page() ) {
// Изменяем запрос для инъекции контента после каждых X постов
add_filter( 'the_posts', 'fs_insert_content_after_posts', 10, 2 );
function fs_insert_content_after_posts( $posts, $query ) {
// Избавляемся от фильтра для административной панели
if ( is_admin() ) return $posts;
// Только для основного запроса
if ( ! is_main_query() ) return $posts;
// Определяем частоту вставки
$frequency = 5; // здесь укажите после какого поста хотите вставлять контент (например, после каждого 5-го поста)
// Проверяем существует ли массив постов
if ( empty( $posts ) ) return $posts;
// Инъекция контента
$injected_content = '<article class="injected-content"><div class="content">Ваш контент здесь</div></article>';
$new_posts = [];
foreach ( $posts as $index => $post ) {
$new_posts[] = $post; // добавляем пост
// Каждый раз, когда текущий индекс + 1 (так как индексы начинаются с 0) кратен частоте, добавляем контент
if ( ( $index + 1 ) % $frequency === 0 ) {
$new_posts[] = $injected_content; // добавляем инъекцию
}
}
return $new_posts;
} // Конец функции fs_insert_content_after_posts
} // Конец условия для главной страницы
} // Конец функции fs_only_do_on_homepage
Пояснение к этому коду:
-
Фильтр
the_posts
: Мы используем этот фильтр для изменения массива постов после того, как WP_Query собрал их, но перед их выводом. Этот подход гарантирует, что мы не затрагиваем данные постов, а просто добавляем дополнительный контент там, где это необходимо. -
Частота вставки: Переменная
$frequency
определяет, после какого поста будет заноситься инъекционный контент. В нашем примере контент будет добавляться после каждого пятого поста. -
Создание нового массива: После выполнения основного цикла мы создаём новый массив, добавляя в него как посты, так и инъекционные элементы на основе заданной частоты.
Этот подход даст вам большую гибкость в управлении выводом постов и инъекцией контента, не зависимо от пагинации. Вы можете легко настроить частоту, изменяя значение переменной $frequency
, а также изменить сам контент, который будет инъектироваться.