Расширение WP Query: Пользовательские значения геолокации работают, но tax_query не работает.

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

Я создал пользовательский запрос для геолокационных поисков. Я сохраняю широту и долготу в каждом пользовательском типе записи как мета-значение. Мое расширение запроса успешно возвращает результаты, когда широта и долгота являются единственными переменными поиска.

Проблема: Я хотел бы также добавить tax_queries к моему запросу для дальнейшего выбора записей.

Класс для расширения запроса:

class WP_Query_Geo extends WP_Query {

  function __construct( $args = array() ) {

    if(!empty($args['lat'])) {

      $this->lat = $args['lat'];
      $this->lng = $args['lng'];
      $this->distance = $args['distance'];
      $this->lat_meta_name = $args[ 'lat_meta_name' ];
      $this->lng_meta_name = $args[ 'lng_meta_name' ];
      $this->orderby = $args[ 'orderby' ];
      $this->unit_of_measure = 3959;

      add_filter('posts_fields', array($this, 'posts_fields'));
      add_filter('posts_join', array($this, 'posts_join'));
      add_filter('posts_where', array($this, 'posts_where'));
      add_filter('posts_orderby', array($this, 'posts_orderby'));

    }

    parent::query($args);

    remove_filter('posts_fields', array($this, 'posts_fields'));
    remove_filter('posts_join', array($this, 'posts_join'));
    remove_filter('posts_where', array($this, 'posts_where'));
    remove_filter('posts_orderby', array($this, 'posts_orderby'));

  }

  function posts_fields($fields) {
    global $wpdb;
    $fields = $wpdb->prepare(" $wpdb->posts.*, pm1.meta_value, pm2.meta_value,
    ACOS(SIN(RADIANS(%f))*SIN(RADIANS(pm1.meta_value))+COS(RADIANS(%f))*COS(RADIANS(pm1.meta_value))*COS(RADIANS(pm2.meta_value)-RADIANS(%f))) * %d AS distance ", $this->lat, $this->lat, $this->lng, $this->unit_of_measure);
    return $fields;
  }

  function posts_join($join) {
    global $wpdb;
    $join .= " INNER JOIN $wpdb->postmeta pm1 ON ($wpdb->posts.id = pm1.post_id AND pm1.meta_key = '".$this->lat_meta_name."')";
    $join .= " INNER JOIN $wpdb->postmeta pm2 ON ($wpdb->posts.id = pm2.post_id AND pm2.meta_key = '".$this->lng_meta_name."')";
    return $join;
  }

  function posts_where($where) {
    global $wpdb;
    $where .= $wpdb->prepare(" HAVING distance < %d ", $this->distance);
    return $where;
  }

  function posts_orderby($orderby) {
    if($this->orderby == 'distance') $orderby = " distance ASC, " . $orderby;
    return $orderby;
  }

}

 

Запрос:

$args = array(
    'post_type'      => 'custom',
    'posts_per_page' => 15,
    'post_status'    => 'publish',
    'paged'          => $page,
    'orderby'        => 'distance',
    'lat'            => $lat,
    'lng'            => $lng,
    'distance'       => $distance,
);

$the_query = new WP_Query_Geo( $args );

 

Вышеуказанное работает. Как только я добавляю налоговый запрос, я получаю ошибку.

Пример налогового запроса:

  $tax_query_args = array( 'relation' => 'AND' );
  $tax_to_push = array(
      'taxonomy' => 'custom-tax',                
      'field'    => 'id',                    
      'terms'    => array(1,2,3),
      'operator' => 'IN'                   
  );
  array_push($tax_query_args, $tax_to_push);
  $args['tax_query'] = $tax_query_args; 

 

Сообщение об ошибке:

Ошибка базы данных WordPress У вас есть ошибка в синтаксисе SQL; проверьте руководство, которое соответствует вашей версии сервера MySQL для правильного синтаксиса к использованию рядом с 'GROUP BY wp_posts.ID ORDER BY distance ASC, wp_posts.post_date DESC LIMIT 0, 15' в строке 4 для запроса
SELECT SQL_CALC_FOUND_ROWS wp_posts.*, pm1.meta_value, pm2.meta_value,\n ACOS(SIN(RADIANS(42.407211))*SIN(RADIANS(pm1.meta_value))+COS(RADIANS(42.407211))*COS(RADIANS(pm1.meta_value))*COS(RADIANS(pm2.meta_value)-RADIANS(-71.382437))) * 3959 AS distance
FROM wp_posts
INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_postmeta pm1 ON (wp_posts.id = pm1.post_id AND pm1.meta_key = 'office_lat')
INNER JOIN wp_postmeta pm2 ON (wp_posts.id = pm2.post_id AND pm2.meta_key = 'office_lng')
WHERE 1=1
    AND ( \n wp_term_relationships.term_taxonomy_id IN (26)\n)
    AND wp_posts.post_type="clinicians"
    AND ((wp_posts.post_status="publish"))
HAVING distance < 50
GROUP BY wp_posts.ID
ORDER BY distance ASC, wp_posts.post_date DESC
LIMIT 0, 15

Я нашел решение. Не знаю, является ли это лучшим решением, но оно работает.

GROUP BY wp_posts.ID вызывало ошибку в моем запросе. Я не совсем понимаю, почему. В теории мой запрос должен возвращать дубликаты записей без него… но этого не происходит. Поэтому я собираюсь опубликовать этот код, так как он работает как задумано.

Для согласованности, это фильтр и соответствующая функция, которую я добавил в свою функцию конструктора:

add_filter('posts_groupby', array($this, 'my_posts_groupby'));

function my_posts_groupby($groupby) {
    global $wpdb;
    $groupby = "";
    return $groupby;
}

Для получения дополнительной информации читайте Фильтр GROUP BY в WP ORG codex.

Нашел решение вашей проблемы… WordPress ставит $groupby после $where, — в вашем примере это вызывает ошибку MySQL, так как GROUP BY идет перед HAVING в синтаксисе MySQL. Решение — добавить GROUP BY внутри выражения $where в функции posts_where 🙂

Ответ или решение

Расширение WP_Query: Параметры геолокации и tax_query

Разработка кастомного запроса в WordPress, который объединяет геолокационные параметры и таксономии, представляет собой сложную задачу. Пользуясь вашим кодом, можно добиться желаемого результата, но необходимо учитывать некоторые тонкости.

Описание проблемы

Вы уже создали класс WP_Query_Geo, который корректно выполняет запросы, основанные на широте и долготе. Проблема возникает при добавлении таксового запроса (tax_query), что приводит к ошибке синтаксиса SQL из-за неправильно сформированного запроса.

Основная ошибка заключается в том, что команда GROUP BY в SQL должна идти до HAVING, однако WordPress вставляет GROUP BY после WHERE и HAVING, что приводит к синтаксической ошибке:

...HAVING distance < 50
GROUP BY wp_posts.ID
ORDER BY distance ASC, wp_posts.post_date DESC

Решение проблемы

Вы нашли одно из решений, отключив GROUP BY при помощи фильтра:

add_filter('posts_groupby', array($this, 'my_posts_groupby'));

function my_posts_groupby($groupby) {
    global $wpdb;
    $groupby = "";
    return $groupby;
}

Это решение убирает GROUP BY, но также допускает возможность дублирования записей, что может быть нежелательно. Хотя это не вызывает ошибок в SQL, важно понимать возможные последствия.

Согласно вашему наблюдению, если группировка по ID постов не приводит к ошибкам, это может означать, что ваши результаты не содержат дубликатов. Однако, если дубликаты появятся, вам необходимо будет вернуться к данному пункту.

Чтобы избежать ошибок в SQL, лучше добавить условие GROUP BY в функцию posts_where, что обеспечит более корректное выполнение запроса:

function posts_where($where) {
    global $wpdb;
    $where .= $wpdb->prepare(" HAVING distance < %d ", $this->distance);
    $where .= " GROUP BY wp_posts.ID "; // Теперь GROUP BY будет перед HAVING
    return $where;
}

Этот подход поможет вам избежать конфликта и корректно сформирует запрос, обеспечивая необходимую функциональность и производительность.

Итог

Таким образом, при разработке кастомного WP запроса с использованием геолокационных мета-закладок и таксовых запросов важно учитывать порядок выполнения SQL-команд. Использование фильтра posts_groupby может быть временным обходным решением, но для надежности стоит проанализировать способ включения GROUP BY в вашу posts_where методику.

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

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

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