Фильтрация по нескольким пользовательским полям в WP REST API 2

14 мая 2016 г., 08:57:12
Просмотры: 16.3K
Голосов: 18

Я хочу фильтровать записи по нескольким пользовательским полям ACF с условием AND. Примерно так:

$args = array(
        'post_type'  => 'product',
        'meta_query' => array(
            'relation' => 'AND',
            array(
                'key'     => 'color',
                'value'   => 'blue',
                'compare' => '=',
            ),
            array(
                'key'     => 'price',
                'value'   => array( 20, 100 ),
                'type'    => 'numeric',
                'compare' => 'BETWEEN',
            ),
        ),
    );

Фильтров может быть даже больше. Как преобразовать это в фильтры для REST API 2?

1
Комментарии

Посмотрите этот пост и попробуйте создать свою функцию http://wordpress.stackexchange.com/questions/169408/wp-json-rest-api-ryan-mccue-how-to-query-posts-with-specific-meta-data-with-a

emilushi emilushi
25 мая 2016 г. 16:03:00
Все ответы на вопрос 5
0

Это решение работает с get_items() в файле /lib/endpoints/class-wp-rest-posts-controller.php v2 WP REST API.


Сначала вам нужно сформировать аргументы GET, как вы бы сделали для new WP_Query(). Проще всего это сделать с помощью http_build_query().

$args = array (
    'filter' => array (
        'meta_query' => array (
            'relation' => 'AND',
            array (
                'key'     => 'color',
                'value'   => 'blue',
                'compare' => '=',
            ),
            array (
                'key'     => 'test',
                'value'   => 'testing',
                'compare' => '=',
            ),
        ),
    ),
);
$field_string = http_build_query( $args );

Результат будет примерно таким:

filter%5Bmeta_query%5D%5Brelation%5D=AND&filter%5Bmeta_query%5D%5B0%5D%5Bkey%5D=color&filter%5Bmeta_query%5D%5B0%5D%5Bvalue%5D=blue&filter%5Bmeta_query%5D%5B0%5D%5Bcompare%5D=%3D&filter%5Bmeta_query%5D%5B1%5D%5Bkey%5D=test&filter%5Bmeta_query%5D%5B1%5D%5Bvalue%5D=testing&filter%5Bmeta_query%5D%5B1%5D%5Bcompare%5D=%3D

Если вам нужен читаемый вариант, вы можете использовать инструменты Chrome и функцию decodeURIComponent('ваш-запрос-здесь'), чтобы сделать его более удобным для чтения при добавлении в URL JSON REST API:

https://demo.wp-api.org/wp-json/wp/v2/product?filter[meta_query][relation]=AND&filter[meta_query][0][key]=color&filter[meta_query][0][value]=blue&filter[meta_query][0][compare]==&filter[meta_query][1][key]=test&filter[meta_query][1][value]=testing&filter[meta_query][1][compare]==

Примечание: Для использования вашего пользовательского типа записи укажите product перед ?

/wp-json/wp/v2/<пользовательский-тип-записи>?filter[meta_query]


Итак, у вас есть запрос, но нам нужно указать WordPress, как обрабатывать несколько моментов:

  1. Добавление поддержки REST для пользовательского типа записи product
  2. Разрешение аргументов запроса meta_query
  3. Обработка meta_query

// 1) Добавление поддержки CPT <product>


function wpse_20160526_add_product_rest_support() {
    global $wp_post_types;

    //Убедитесь, что указано имя вашего типа записи!
    $post_type_name = 'product';
    if( isset( $wp_post_types[ $post_type_name ] ) ) {
        $wp_post_types[$post_type_name]->show_in_rest = true;
        $wp_post_types[$post_type_name]->rest_base = $post_type_name;
        $wp_post_types[$post_type_name]->rest_controller_class = 'WP_REST_Posts_Controller';
    }
}

add_action( 'init', 'wpse_20160526_add_product_rest_support', 25 );


// 2) Добавление поддержки `meta_query` в GET-запросе

function wpse_20160526_rest_query_vars( $valid_vars ) {
    $valid_vars = array_merge( $valid_vars, array(  'meta_query'  ) ); // Пропустите meta_key, meta_value, если они вам не нужны
    return $valid_vars;
}

add_filter( 'rest_query_vars', 'wpse_20160526_rest_query_vars', PHP_INT_MAX, 1 );


// 3) Обработка пользовательских аргументов

function wpse_20160526_rest_product_query( $args, $request ) {

    if ( isset( $args[ 'meta_query' ] ) ) {

        $relation = 'AND';
        if( isset($args['meta_query']['relation']) && in_array($args['meta_query']['relation'], array('AND', 'OR'))) {
            $relation = sanitize_text_field( $args['meta_query']['relation'] );
        }
        $meta_query = array(
            'relation' => $relation
        );

        foreach ( $args['meta_query'] as $inx => $query_req ) {
        /*
            Массив (

                [key] => test
                [value] => testing
                [compare] => =
            )
        */
            $query = array();

            if( is_numeric($inx)) {

                if( isset($query_req['key'])) {
                    $query['key'] = sanitize_text_field($query_req['key']);
                }
                if( isset($query_req['value'])) {
                    $query['value'] = sanitize_text_field($query_req['value']);
                }
                if( isset($query_req['type'])) {
                    $query['type'] = sanitize_text_field($query_req['type']);
                }
                if( isset($query_req['compare']) && in_array($query_req['compare'], array('=', '!=', '>','>=','<','<=','LIKE','NOT LIKE','IN','NOT IN','BETWEEN','NOT BETWEEN', 'NOT EXISTS')) ) {
                    $query['compare'] = sanitize_text_field($query_req['compare']);
                }
            }

            if( ! empty($query) ) $meta_query[] = $query;
        }

        // Заменяем на обработанные аргументы запроса
        $args['meta_query'] = $meta_query;
    }

    return $args;
}
add_action( 'rest_product_query', 'wpse_20160526_rest_product_query', 10, 2 );
26 мая 2016 г. 08:00:23
8

Вот тест, который я провел на локальном сервере:

По соображениям безопасности meta_query не разрешен в WP API. Первое, что нужно сделать — добавить meta_query в разрешенные rest_query, добавив эту функцию в файл темы WordPress functions.php:

function api_allow_meta_query( $valid_vars ) {

  $valid_vars = array_merge( $valid_vars, array( 'meta_query') );
  return $valid_vars;
}
add_filter( 'rest_query_vars', 'api_allow_meta_query' );

После этого вам нужно построить HTML-запрос, используя эту функцию на другом сайте, который будет получать данные с WordPress-сайта:

$curl = curl_init();
$fields = array (
  'filter[meta_query]' => array (
    'relation' => 'AND',
      array (
        'key' => 'color',
        'value' => 'blue',
        'compare' => '='
      ),
      array (
        'key' => 'price',
        'value' => array ( 20, 100 ),
        'type' => 'numeric',
        'compare' => 'BETWEEN'
      ),
    ),
  );

$field_string = http_build_query($fields);

curl_setopt_array($curl, array (
    CURLOPT_RETURNTRANSFER => 1,
    CURLOPT_URL => 'http://yourwordpreswebssite.com/wp-json/wp/v2/posts?' . $field_string
  )
);

$result = curl_exec($curl);

echo htmlentities($result);

Я изменил массив полей, чтобы он выглядел как ваши аргументы запроса. Закодированная строка запроса будет выглядеть так:

http://yourwordpreswebssite.com/wp-json/wp/v2/posts?filter%5Btaxonomy%5D=product&filter%5Bmeta_query%5D%5Brelation%5D=AND&filter%5Bmeta_query%5D%5B0%5D%5Bkey%5D=color&filter%5Bmeta_query%5D%5B0%5D%5Bvalue%5D=blue&filter%5Bmeta_query%5D%5B0%5D%5Bcompare%5D=%3D&filter%5Bmeta_query%5D%5B1%5D%5Bkey%5D=price&filter%5Bmeta_query%5D%5B1%5D%5Bvalue%5D%5B0%5D=20&filter%5Bmeta_query%5D%5B1%5D%5Bvalue%5D%5B1%5D=100&filter%5Bmeta_query%5D%5B1%5D%5Btype%5D=numeric&filter%5Bmeta_query%5D%5B1%5D%5Bcompare%5D=BETWEEN

Использование urldecode(), в данном случае: urldecode('http://yourwordpreswebssite.com/wp-json/wp/v2/posts?' . $field_string);, даст URL вида:

http://yourwordpreswebssite.com/wp-json/wp/v2/posts?filter[taxonomy]=product&filter[meta_query][relation]=AND&filter[meta_query][0][key]=color&filter[meta_query][0][value]=blue&filter[meta_query][0][compare]==&filter[meta_query][1][key]=price&filter[meta_query][1][value][0]=20&filter[meta_query][1][value][1]=100&filter[meta_query][1][type]=numeric&filter[meta_query][1][compare]=BETWEEN

Если вы можете предоставить URL вашего рабочего сайта, мы сможем протестировать его напрямую с помощью Postman, поскольку для тестирования на локальном сервере или любом существующем WordPress-сайте потребуется создание пользовательского типа записей для товаров и добавление метаполей и т.д. Удачи!

25 мая 2016 г. 18:07:09
Комментарии

Спасибо за ответ, но я проверил запрос, как в вопросе, в Postman, и он не сработал.

James Vu James Vu
26 мая 2016 г. 04:22:15

@Dan я внес некоторые улучшения в решение, значения фильтров совпадают с вашими аргументами запроса, включая пользовательский тип записи, который не был указан в предыдущем решении.

emilushi emilushi
26 мая 2016 г. 09:54:20

У нас нет таксономии product. Работает отлично! Не подумал обернуть meta_query внутри filter :)

James Vu James Vu
26 мая 2016 г. 13:33:40

@Dan Рад это слышать. Вчера я написал пост на эту тему, возможно, тебе будет интересно поделиться им :) WordPress REST API с метаполями.

emilushi emilushi
26 мая 2016 г. 13:45:10

Несколько замечаний: на некоторых серверах AWS использование [] для массивов может привести к ошибке запроса. Лучше использовать array() для надежности и для тех, кто может копировать/вставлять код. Также, поддерживает ли это CPT product или только таксономию? И последнее: нужно ли санитизировать meta_query? Учитывая, что данные берутся напрямую, есть ли риск безопасности при принятии любых пользовательских данных?

jgraup jgraup
26 мая 2016 г. 15:12:41

@Eduart Я знаю, как заставить это работать для себя на основе твоей идеи, но тебе тоже стоит отредактировать ответ.

James Vu James Vu
26 мая 2016 г. 16:19:55

@jgraup большое спасибо за ваш комментарий. Я не знал этого факта про сервера AWS. Пользовательские типы записей и пользовательские таксономии должны быть зарегистрированы с REST API заранее, и конечно же должна использоваться валидация на ввод и экранирование на вывод. Причина, по которой я не использовал sanitize, в том, что я тестировал это только на выводе для пользовательских полей, которые я предварительно валидировал на вводе. Но вы правы, это стоит улучшить для других пользователей, которые просто скопируют и вставят код.

emilushi emilushi
26 мая 2016 г. 21:33:19

Некоторые сервера AWS поддерживают только PHP 5.3, что не очень хорошо, но что есть, то есть. Так что это синтаксическая ошибка в 5.3.

jgraup jgraup
26 мая 2016 г. 22:04:52
Показать остальные 3 комментариев
0
  1. Сначала добавьте плагин или скопируйте весь код и вставьте его в functions.php по ссылке https://github.com/WP-API/rest-filter
  2. Используйте следующий запрос:

?filter[meta_query][relation]=AND
&filter[meta_query][0][key]=REAL_HOMES_property_price
&filter[meta_query][0][value][0]=10
&filter[meta_query][0][value][1]=10000001
&filter[meta_query][0][compare]=''
&filter[meta_query][1][key]=REAL_HOMES_property_price
&filter[meta_query][1][value]=10
&filter[meta_query][1][compare]='='

7 апр. 2020 г. 12:51:48
2

Вы можете сделать это без Rest API Вот так (Это мой фильтр записей)

$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args = array(
        'paged' => $paged,
        'orderby' => 'date', // сортировка по дате у нас будет в любом случае (но вы можете изменить/доработать это)
        'order' => 'DESC',
    );

    // создаём массив $args['meta_query'] если указана хотя бы одна цена или отмечен чекбокс
    if( isset( $_GET['price_min'] ) || isset( $_GET['price_max'] ) || isset( $_GET['type'] ) )
        $args['meta_query'] = array( 'relation'=>'AND' ); // AND значит все условия meta_query должны выполняться


    if( $type ){
        $args['meta_query'][] = array(
            'key' => 'type',
            'value' => $type,
        );
    };

    if( $plan ){
        $args['meta_query'][] = array(
            'key' => 'plan',
            'value' => $plan,
        );
    };

    if( $room_num ){
        $args['meta_query'][] = array(
            'key' => 'room_num',
            'value' => $room_num,
        );
    };

    if( $etage ){
        $args['meta_query'][] = array(
            'key' => 'etage',
            'value' => $etage,
        );
    };  

    if( $price_min || $price_max ){
        $args['meta_query'][] = array(
            'key' => 'price',
            'value' => array( $price_min, $price_max ),
            'type' => 'numeric',
            'compare' => 'BETWEEN'
        );
    };  

    if( $area_min || $area_max ){
        $args['meta_query'][] = array(
            'key' => 'area',
            'value' => array( $area_min, $area_max ),
            'type' => 'numeric',
            'compare' => 'BETWEEN'
        );
    };
25 мая 2016 г. 17:20:09
Комментарии

Спасибо за ответ, но мне очень интересно сделать это через REST API v2.

James Vu James Vu
25 мая 2016 г. 17:27:06

Ну, я считаю, мой вариант хорош, но если вам хочется... Дело в том, что мой метод не ограничен параметрами!

Igor Fedorov Igor Fedorov
25 мая 2016 г. 17:31:35
0

В WordPress 4.7 аргумент filter был удалён.

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

Пока что я не нашёл решения, как сделать то же самое без установки плагина.

12 янв. 2017 г. 07:51:54