WP_Query внутри цикла foreach возвращает одно и то же значение для всех опций при фильтрации с помощью ajax.

Вопрос или проблема

Я пытаюсь создать расписание для отображения классов по времени. У меня есть таксономия Дня, по которой я перебираю, чтобы создать колонку для каждого дня недели. Внутри колонки используется 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. Однако данное решение может быть улучшено для избежания хардкода, что приведет к более чистому и поддерживаемому коду.

Шаги по исправлению:

  1. Используйте вложенные WP_Query: В рамках AJAX-запроса используйте отдельные запросы для каждой итерации дня. Это позволит корректно фильтровать классы по дням недели.

  2. Правильная организация таксономий: Проверьте и удостоверьтесь, что таксономии устанавливаются и использованы правильно внутри WP_Query. Например, убедитесь, что ‘calendar-day’ используется корректно в каждом запросе.

  3. Массив 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'],
        ),
    );
  4. Удалить дублирование кода: Добавьте цикл foreach для перебора array дней недели и использования WP_Query внутри данного цикла без необходимости хардкодинга для каждого дня отдельно:

    foreach ($calendarDays as $day) {
        $args = // ваши аргументы запроса
        $loop = new WP_Query($args);
    
        // Отображаете класс, согласно дню
    }
  5. Проверка после выполнения цикла: Убедитесь, что вызов wp_reset_postdata() корректно используется после каждой реализации WP_Query, чтобы избежать перекрытия глобальных переменных.

Итог

Внедрение вышеупомянутых улучшений позволит избежать проблем с дублированием результатов в каждой итерации цикла foreach. Таким образом, ваш AJAX будет вызывать правильные данные для каждого дня недели и корректно отображать классы в их соответствующих колонках. Этот подход оптимизирован с учетом поддержки и ясности кода, что в дальнейшем облегчит внесение изменений и улучшений.

Успехов в реализации, и если возникнут дополнительные вопросы, пожалуйста, обращайтесь!

Оцените материал
Добавить комментарий

Капча загружается...