Вопрос или проблема
Я пытаюсь создать расписание для отображения классов по времени. У меня есть таксономия Дня, по которой я перебираю, чтобы создать колонку для каждого дня недели. Внутри колонки используется WP_Query, который фильтрует по таксономии дня и meta_key
classes_entry_start_time
, чтобы отсортировать по meta_value_num
. Затем расписание фильтруется с помощью ajax.
$calendarDays = get_terms([
'taxonomy' => 'calendar-day',
'hide_empty' => false,
]);
$calendarDays_count = count( $calendarDays );
if ( $calendarDays_count > 0 && is_array( $calendarDays ) ) {
echo '<div class="classes-calendar">';
// Отображение фильтра
$classesFilter = array_map(function($class) {
return [
'value' => $class->term_id,
'title' => $class->name
];
}, get_terms('calendar-class-type'));
get_part('classes-filter', [
'id' => 'classes-filter',
'title' => 'All',
'items' => array_merge( $classesFilter )
]);
// Перебор таксономии дня
echo '<div id="response" class="container classes-wrap">';
foreach ( $calendarDays as $day ) {
echo '<div class="classes-col">';
echo '<strong>' . $day->name . '</strong>';
$args = array(
'post_type' => 'calendar',
'meta_key' => 'classes_entry_start_time',
'orderby' => 'meta_value_num',
'order' => 'ASC',
'posts_per_page' => '-1',
'tax_query' => array(
array(
'taxonomy' => 'calendar-day',
'field' => 'id',
'terms' => $day->term_id,
)
),
);
// echo '<div id="response" class="container">';
echo '<ul id="" class="response">';
$query = new WP_Query( $args );
if( $query->have_posts() ):
while( $query->have_posts() ):
$query->the_post();
get_part('classes-item', ['classesEntry' => get_field('classes_entry')]);
endwhile;
else:
echo 'No data to show';
endif;
wp_reset_query();
echo '</ul>';
// echo '</div>'; // END response
echo '</div>'; // END classes-col
} // END foreach
echo '</div>'; // END response
echo '<div class="section container center">';
echo '<a href="#" class="btn">Book class</a>';
echo '</div>';
echo '</div>'; // END classes-calendar
} // END if
Основной запрос работает так, как ожидалось, и ajax-запрос возвращает правильные значения, но только когда я не использую цикл foreach
в ajax-обработчике.
Ниже приведена функция обработчика ajax, которая получает правильные записи на основе выбранного фильтра, но отображает один и тот же результат для всех опций.
Цикл foreach основан на таксономии, которая отображает колонки, представляющие каждый день недели: с понедельника по воскресенье.
Например, он возвращает:
Понедельник: Пост один Вторник: Пост один Среда: Пост один …
Но на самом деле он должен возвращать:
Понедельник: Пост один, Пост два Вторник: Пост три Среда: Пост четыре …
Как лучше всего фильтровать WP_Query на основе двух таксономий и также отображать запрос в циклах foreach так, чтобы фильтр показывал только посты, назначенные типам фильтров, и отображал их в правильной колонке дня, не обновляя все колонки цикла foreach
?
Использовать ли цикл foreach неправильно для того, что я пытаюсь достичь?
Фильтрация запроса, где есть проблема
add_action('wp_ajax_classfilter', 'load_posts_by_ajax_callback');
add_action('wp_ajax_nopriv_classfilter', 'load_posts_by_ajax_callback');
function load_posts_by_ajax_callback() {
check_ajax_referer('load_more_posts', 'security');
$calendarDays = get_terms([
'taxonomy' => 'calendar-day',
'hide_empty' => false,
]);
foreach($calendarDays as $day) {
$args = array(
'post_type' => 'calendar',
'post_status' => 'publish',
'meta_key' => 'classes_entry_start_time',
'orderby' => 'meta_value_num',
'order' => 'ASC',
'tax_query' => array(
array(
'taxonomy' => 'calendar-day',
'field' => 'id',
'terms' => $day->term_id
)
)
);
if( isset( $_POST['theClass'] ) )
$args['tax_query'] = array(
array(
'taxonomy' => 'calendar-class-type',
'field' => 'id',
'terms' => $_POST['theClass']
)
);
$loop = new WP_Query($args);
if($loop->have_posts()) {
echo '<div class="classes-col">';
echo '<strong>' . $day->name . '</strong>';
echo '<ul>';
while($loop->have_posts()) : $loop->the_post();
get_part('classes-item', ['classesEntry' => get_field('classes_entry')]);
endwhile;
wp_reset_postdata();
echo '</ul>';
echo '</div>';
}
else {
echo '<div class="classes-col">';
echo '<strong>' . $day->name . '</strong>';
echo '</div>';
}
}
wp_die();
}
Если кто-то может указать мне нужное направление, это было бы здорово. Я потратил дни на попытки разных вариантов без успеха! Единственный способ, которым я могу заставить это работать, как надо – это полностью удалить цикл foreach, чего я не могу сделать, так как мне нужны колонки недели.
В качестве ссылки, вот ajax запрос
import axios from 'axios';
const classesFilter = () => {
const el = document.querySelector('#classes-filter');
if (el) {
el.addEventListener('change', e => {
e.preventDefault();
const formData = new FormData(el);
formData.append('action', 'classfilter');
formData.append('security', WP.security);
// Запрос POST через Axios
axios({
method: 'POST',
url: WP.ajax,
data: formData,
})
// обработка успешного ответа
.then(function (response) {
console.log(response.data);
console.log(response.status);
document.getElementById("response").innerHTML = response.data;
// document.getElementsByClassName("response").innerHTML = response.data;
},
(error) => {
console.log(error);
});
});
}
};
export default classesFilter;```
Если кому-то интересно, как достичь результата для вопроса, который я задал, я смог это понять!
Поскольку правильные результаты были отображены, но для каждой итерации цикла foreach, а не конкретного дня, мне удалось отобразить/скрыть результаты, используя if (is_object_in_term($class->ID, 'calendar-day', 'monday') && $day->term_id == 6)
. is_object_in_term
проверяет, совпадают ли $taxonomy
с id, именами и слагами терминов объекта и $day->term_id
для каждого дня. Если нет, то ничего не отображается.
Мне пришлось жестко закодировать каждый день недели и код для генерации результата. Я знаю, что это не лучший подход D.R.Y, но на данный момент я доволен текущим достижением!
add_action('wp_ajax_classfilter', 'load_posts_by_ajax_callback');
add_action('wp_ajax_nopriv_classfilter', 'load_posts_by_ajax_callback');
function load_posts_by_ajax_callback() {
check_ajax_referer('load_more_posts', 'security');
$days = get_terms([
'taxonomy' => 'calendar-day'
]);
// Перебор дней недели
foreach ($days as $day ) {
$classesTable .= '<div class="response classes-col">';
$classesTable .= '<strong>' . $day->name . '</strong>'; // Название дня
// Параметры запроса на основе дня недели
$args = array(
'post_type' => 'calendar',
'meta_key' => 'classes_entry_start_time',
'orderby' => 'meta_value_num',
'order' => 'ASC',
'numberposts' => '-1',
'tax_query' => array(
array(
'taxonomy' => 'calendar-day',
'field' => 'id',
'terms' => $day->term_id
// 'terms' => array('6', '7', '8', '9', '10', '11', '12')
)
),
);
if(isset( $_POST['theClass'])) {
$args['tax_query'] = array(
array(
'taxonomy' => 'calendar-class-type',
'field' => 'id',
'terms' => $_POST['theClass']
)
);
}
$query_classes = get_posts( $args );
$classesArray = array();
// Перебор постов запроса классов
foreach ($query_classes as $key => $class ) {
// Проверка, есть ли у объекта термин: Понедельник
if (is_object_in_term($class->ID, 'calendar-day', 'monday') && $day->term_id == 6):
// $classesArray[] .= 'YES';
$start_time = get_field('classes_entry_start_time', $class);
$end_time = get_field('classes_entry_end_time', $class);
$classesEntry = get_field('classes_entry', $class);
// Создание элемента класса
$classesArray[] .= '<li>';
$classesArray[] .= '<a href="' . $classesEntry['booking_app_link'] . '" target="_blank" class="classes-item">';
$classesArray[] .= '<span>' . $classesEntry['start_time'] . '</span>';
$classesArray[] .= '<span>' . $class->post_title . '</span>';
$classesArray[] .= '<span>' . $classesEntry['duration'] . '</span>';
$terms = get_the_terms( $class->ID , 'calendar-class-type' );
if ($terms): foreach ( $terms as $term ):
$classesArray[] .= '<span>' . $term->name . '</span>';
endforeach; endif;
$classesArray[] .= '</a>';
$classesArray[] .= '</li>';
else:
// Ничего не показывать
// $classesArray[] .= 'NO';
endif;
// Проверка, есть ли у объекта термин: Вторник
if (is_object_in_term($class->ID, 'calendar-day', 'tuesday') && $day->term_id == 7):
// $classesArray[] .= 'YES';
$start_time = get_field('classes_entry_start_time', $class);
$end_time = get_field('classes_entry_end_time', $class);
$classesEntry = get_field('classes_entry', $class);
// Создание элемента класса
$classesArray[] .= '<li>';
$classesArray[] .= '<a href="' . $classesEntry['booking_app_link'] . '" target="_blank" class="classes-item">';
$classesArray[] .= '<span>' . $classesEntry['start_time'] . '</span>';
$classesArray[] .= '<span>' . $class->post_title . '</span>';
$classesArray[] .= '<span>' . $classesEntry['duration'] . '</span>';
$terms = get_the_terms( $class->ID , 'calendar-class-type' );
if ($terms): foreach ( $terms as $term ):
$classesArray[] .= '<span>' . $term->name . '</span>';
endforeach; endif;
$classesArray[] .= '</a>';
$classesArray[] .= '</лі>';
else :
// Ничего не показывать
// $classesArray[] .= 'NO';
endif;
// Проверка, есть ли у объекта термин: Среда
if (is_object_in_term($class->ID, 'calendar-day', 'wednesday') && $day->term_id == 8):
// $classesArray[] .= 'YES';
$start_time = get_field('classes_entry_start_time', $class);
$end_time = get_field('classes_entry_end_time', $class);
$classesEntry = get_field('classes_entry', $class);
// Создание элемента класса
$classesArray[] .= '<li>';
$classesArray[] .= '<a href="' . $classesEntry['booking_app_link'] . '" target="_blank" class="classes-item">';
$classesArray[] .= '<span>' . $classesEntry['start_time'] . '</span>';
$classesArray[] .= '<span>' . $class->post_title . '</span>';
$classesArray[] .= '<span>' . $classesEntry['duration'] . '</span>';
$terms = get_the_terms( $class->ID , 'calendar-class-type' );
if ($terms): foreach ( $terms as $term ):
$classesArray[] .= '<span>' . $term->name . '</span>';
endforeach; endif;
$classesArray[] .= '</a>';
$classesArray[] .= '</li>';
else :
// Ничего не показывать
// $classesArray[] .= 'NO';
endif;
// Проверка, есть ли у объекта термин: Четверг
if (is_object_in_term($class->ID, 'calendar-day', 'thursday') && $day->term_id == 9):
// $classesArray[] .= 'YES';
$start_time = get_field('classes_entry_start_time', $class);
$end_time = get_field('classes_entry_end_time', $class);
$classesEntry = get_field('classes_entry', $class);
// Создание элемента класса
$classesArray[] .= '<li>';
$classesArray[] .= '<a href="' . $classesEntry['booking_app_link'] . '" target="_blank" class="classes-item">';
$classesArray[] .= '<span>' . $classesEntry['start_time'] . '</span>';
$classesArray[] .= '<span>' . $class->post_title . '</span>';
$classesArray[] .= '<span>' . $classesEntry['duration'] . '</span>';
$terms = get_the_terms( $class->ID , 'calendar-class-type' );
if ($terms): foreach ( $terms as $term ):
$classesArray[] .= '<span>' . $term->name . '</span>';
endforeach; endif;
$classesArray[] .= '</a>';
$classesArray[] .= '</li>';
else :
// Ничего не показывать
// $classesArray[] .= 'NO';
endif;
// Проверка, есть ли у объекта термин: Пятница
if (is_object_in_term($class->ID, 'calendar-day', 'friday') && $day->term_id == 10):
// $classesArray[] .= 'YES';
$start_time = get_field('classes_entry_start_time', $class);
$end_time = get_field('classes_entry_end_time', $class);
$classesEntry = get_field('classes_entry', $class);
// Создание элемента класса
$classesArray[] .= '<li>';
$classesArray[] .= '<a href="' . $classesEntry['booking_app_link'] . '" target="_blank" class="classes-item">';
$classesArray[] .= '<span>' . $classesEntry['start_time'] . '</span>';
$classesArray[] .= '<span>' . $class->post_title . '</span>';
$classesArray[] .= '<span>' . $classesEntry['duration'] . '</span>';
$terms = get_the_terms( $class->ID , 'calendar-class-type' );
if ($terms): foreach ( $terms as $term ):
$classesArray[] .= '<span>' . $term->name . '</span>';
endforeach; endif;
$classesArray[] .= '</a>';
$classesArray[] .= '</li>';
else :
// Ничего не показывать
// $classesArray[] .= 'NO';
endif;
// Проверка, есть ли у объекта термин: Суббота
if (is_object_in_term($class->ID, 'calendar-day', 'saturday') && $day->term_id == 11):
// $classesArray[] .= 'YES';
$start_time = get_field('classes_entry_start_time', $class);
$end_time = get_field('classes_entry_end_time', $class);
$classesEntry = get_field('classes_entry', $class);
// Создание элемента класса
$classesArray[] .= '<li>';
$classesArray[] .= '<a href="' . $classesEntry['booking_app_link'] . '" target="_blank" class="classes-item">';
$classesArray[] .= '<span>' . $classesEntry['start_time'] . '</span>';
$classesArray[] .= '<span>' . $class->post_title . '</span>';
$classesArray[] .= '<span>' . $classesEntry['duration'] . '</span>';
$terms = get_the_terms( $class->ID , 'calendar-class-type' );
if ($terms): foreach ( $terms as $term ):
$classesArray[] .= '<span>' . $term->name . '</span>';
endforeach; endif;
$classesArray[] .= '</a>';
$classesArray[] .= '</li>';
else :
// Ничего не показывать
// $classesArray[] .= 'NO';
endif;
// Проверка, есть ли у объекта термин: Воскресенье
if (is_object_in_term($class->ID, 'calendar-day', 'sunday') && $day->term_id == 12):
// $classesArray[] .= 'YES';
$start_time = get_field('classes_entry_start_time', $class);
$end_time = get_field('classes_entry_end_time', $class);
$classesEntry = get_field('classes_entry', $class);
// Создание элемента класса
$classesArray[] .= '<li>';
$classesArray[] .= '<a href="' . $classesEntry['booking_app_link'] . '" target="_blank" class="classes-item">';
$classesArray[] .= '<span>' . $classesEntry['start_time'] . '</span>';
$classesArray[] .= '<span>' . $class->post_title . '</span>';
$classesArray[] .= '<span>' . $classesEntry['duration'] . '</span>';
$terms = get_the_terms( $class->ID , 'calendar-class-type' );
if ($terms): foreach ( $terms as $term ):
$classesArray[] .= '<span>' . $term->name . '</span>';
endforeach; endif;
$classesArray[] .= '</a>';
$classesArray[] .= '</li>';
else :
// Ничего не показывать
// $classesArray[] .= 'NO';
endif;
}
// Отображение всех полученных результатов класса
$classesTable .= '<ul>' . implode(' ', $classesArray) . '</ul>';
$classesTable .= '</div>'; // END classes-col
}
echo $classesTable;
wp_die();
}
.
Ответ или решение
Ваша задача касается использования WP_Query для формирования расписания классов, которые фильтруются с помощью AJAX и отображаются в колонках для каждого дня недели. Вы столкнулись с проблемой, при которой на запросы AJAX все колонки foreach цикла выводят одинаковые результаты, что приводит к некорректному отображению данных для каждого дня недели.
Проблема
Основная причина, по которой вы видите одинаковые посты для всех дней в результате AJAX-запросов, заключается в неправильном использовании WP_Query внутри foreach цикла. Более того, режим работы цикла может влиять на то, как посты извлекаются и отображаются, что приводит к отсутствию должной фильтрации по дням.
Решение
Чтобы решить возникшую проблему, вы должны убедиться в корректной организации циклов и работе с таксономиями. Вы уже частично решили эту задачу, прибегнув к методу проверки термина с помощью is_object_in_term. Однако данное решение может быть улучшено для избежания хардкода, что приведет к более чистому и поддерживаемому коду.
Шаги по исправлению:
-
Используйте вложенные WP_Query: В рамках AJAX-запроса используйте отдельные запросы для каждой итерации дня. Это позволит корректно фильтровать классы по дням недели.
-
Правильная организация таксономий: Проверьте и удостоверьтесь, что таксономии устанавливаются и использованы правильно внутри WP_Query. Например, убедитесь, что ‘calendar-day’ используется корректно в каждом запросе.
-
Массив tax_query: Вместо замены массива
tax_query
при наличии POST-параметра, используйте параметр ‘relation’ => ‘AND’ для сочетания нескольких таксономий:$args['tax_query'] = array( 'relation' => 'AND', array( 'taxonomy' => 'calendar-day', 'field' => 'id', 'terms' => $day->term_id, ), array( 'taxonomy' => 'calendar-class-type', 'field' => 'id', 'terms' => $_POST['theClass'], ), );
-
Удалить дублирование кода: Добавьте цикл foreach для перебора array дней недели и использования WP_Query внутри данного цикла без необходимости хардкодинга для каждого дня отдельно:
foreach ($calendarDays as $day) { $args = // ваши аргументы запроса $loop = new WP_Query($args); // Отображаете класс, согласно дню }
-
Проверка после выполнения цикла: Убедитесь, что вызов wp_reset_postdata() корректно используется после каждой реализации WP_Query, чтобы избежать перекрытия глобальных переменных.
Итог
Внедрение вышеупомянутых улучшений позволит избежать проблем с дублированием результатов в каждой итерации цикла foreach. Таким образом, ваш AJAX будет вызывать правильные данные для каждого дня недели и корректно отображать классы в их соответствующих колонках. Этот подход оптимизирован с учетом поддержки и ясности кода, что в дальнейшем облегчит внесение изменений и улучшений.
Успехов в реализации, и если возникнут дополнительные вопросы, пожалуйста, обращайтесь!