Filtrar múltiples campos personalizados con WP REST API 2

14 may 2016, 08:57:12
Vistas: 16.3K
Votos: 18

Quiero filtrar publicaciones basadas en múltiples campos personalizados de ACF con relación AND. Algo como esto:

$args = array(
        'post_type'  => 'product', // Tipo de publicación
        'meta_query' => array(
            'relation' => 'AND', // Relación AND entre los filtros
            array(
                'key'     => 'color', // Campo color
                'value'   => 'blue', // Valor a buscar
                'compare' => '=', // Comparación exacta
            ),
            array(
                'key'     => 'price', // Campo precio
                'value'   => array( 20, 100 ), // Rango de valores
                'type'    => 'numeric', // Tipo numérico
                'compare' => 'BETWEEN', // Comparación entre valores
            ),
        ),
    );

Incluso podría tener más filtros. ¿Cómo puedo convertir estos filtros para usarlos en REST API 2?

1
Comentarios

Echa un vistazo a esta publicación e intenta crear tu función 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 may 2016 16:03:00
Todas las respuestas a la pregunta 5
0

Esta solución funciona con get_items() en /lib/endpoints/class-wp-rest-posts-controller.php de la API REST WP v2.


Primero, deberás construir los argumentos GET como lo harías para una new WP_Query(). La forma más fácil de hacer esto es con 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 );

Producirá algo como:

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

Si deseas que sea legible, también puedes usar las herramientas de Chrome y decodeURIComponent('tu-consulta-aquí') para facilitar la lectura cuando la incluyas en tu URL de la API REST JSON:

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]==

Nota: Para usar tu tipo de publicación personalizado, colocarías product antes de ?

/wp-json/wp/v2/<tipo-de-publicacion-personalizado>?filter[meta_query]


Así que ya tienes tu consulta, pero necesitamos instruir a WP sobre cómo manejar algunas cosas:

  1. Agregar soporte REST para el tipo de publicación personalizado product
  2. Permitir los argumentos de consulta meta_query
  3. Analizar meta_query

// 1) Agregar soporte REST para CPT <product>


function wpse_20160526_add_product_rest_support() {
    global $wp_post_types;

    //¡asegúrate de establecer esto al nombre de tu tipo de publicación!
    $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) Agregar soporte para `meta_query` en la solicitud GET

function wpse_20160526_rest_query_vars( $valid_vars ) {
    $valid_vars = array_merge( $valid_vars, array(  'meta_query'  ) ); // Omite meta_key, meta_value si no los necesitas
    return $valid_vars;
}

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


// 3) Analizar argumentos personalizados

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 ) {
        /*
            Array (

                [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;
        }

        // reemplazar con argumentos de consulta saneados
        $args['meta_query'] = $meta_query;
    }

    return $args;
}
add_action( 'rest_product_query', 'wpse_20160526_rest_product_query', 10, 2 );
26 may 2016 08:00:23
8

Aquí está una prueba que realicé en Localhost:

Por razones de seguridad, meta_query no está permitido en la API de WP. Primero, lo que debes hacer es agregar meta_query a las consultas REST permitidas añadiendo esta función en el archivo functions.php de tu tema de WordPress:

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' );

Después de eso, necesitarás construir la consulta HTML usando esta función en el otro sitio web que obtendrá los datos del sitio 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);

Modifiqué el array de campos para que ahora se parezca a tus argumentos de consulta. La cadena de consulta codificada se verá así:

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

Al usar urldecode(), que en este caso sería: urldecode('http://yourwordpreswebssite.com/wp-json/wp/v2/posts?' . $field_string);, obtendrás una URL como esta:

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

Si puedes proporcionarnos la URL de tu sitio web en vivo, podemos probarlo usando Postman directamente en tu sitio, porque para probarlo en localhost o en cualquier sitio WordPress existente sería necesario crear un tipo de publicación personalizado de producto y agregar campos meta, etc. ¡Saludos!

25 may 2016 18:07:09
Comentarios

Gracias por tu respuesta, pero he probado con la consulta como en la pregunta en Postman y no funcionó.

James Vu James Vu
26 may 2016 04:22:15

@Dan hice algunas mejoras en la solución, los valores del filtro son los mismos que tus argumentos de consulta, incluyendo el tipo de post personalizado que no estaba especificado en la solución anterior.

emilushi emilushi
26 may 2016 09:54:20

No tenemos la taxonomía product. ¡Funciona genial! No había pensado en envolver el meta_query dentro del filter :)

James Vu James Vu
26 may 2016 13:33:40

@Dan Me alegra escucharlo. Ayer escribí un post sobre esto, podrías considerar compartirlo :) WordPress REST API con campos meta.

emilushi emilushi
26 may 2016 13:45:10

Un par de cosas, en algunos servidores AWS, usar [] como array puede matar la solicitud. Deberías usar array() para estar seguro y para aquellos que podrían copiar/pegar. Además, ¿esto soporta el CPT product o solo la taxonomía? Y por último, ¿necesitas sanitizar meta_query? Viendo que fue extraído, ¿existe un riesgo de seguridad al aceptar cualquier cosa que proporcione un usuario?

jgraup jgraup
26 may 2016 15:12:41

@Eduart Sé cómo hacer que funcione para mí basándome en tu idea, pero también deberías editar la respuesta.

James Vu James Vu
26 may 2016 16:19:55

@jgraup muchas gracias por tu comentario. No sabía ese dato sobre los servidores de AWS. Los tipos de posts personalizados y la taxonomía personalizada deben registrarse con la API REST previamente y sin duda se debe usar validación en la entrada y escape en la salida. La razón por la que no usé sanitize es porque solo lo probé en la salida para campos personalizados que ya había validado previamente en la entrada. Pero tienes razón, debería mejorarse para otros usuarios que solo copien y peguen el código.

emilushi emilushi
26 may 2016 21:33:19

Algunos servidores de AWS solo permiten PHP 5.3, lo cual no es ideal, pero es lo que hay. Así que es un error de sintaxis en 5.3.

jgraup jgraup
26 may 2016 22:04:52
Mostrar los 3 comentarios restantes
0
  1. Primero añade el plugin o copia todo el código y pégalo en el functions.php del siguiente enlace https://github.com/WP-API/rest-filter
  2. Usa esto

?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 abr 2020 12:51:48
2

Puedes hacerlo sin la API REST Así es como (Es mi filtro de publicaciones)

    $paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args = array(
        'paged' => $paged,
        'orderby' => 'date', // la ordenación por fecha estará siempre presente (pero puedes modificarlo/mejorarlo)
        'order' => 'DESC',
    );

    // creamos el array $args['meta_query'] si se especifica al menos un precio o se marca un checkbox
    if( isset( $_GET['price_min'] ) || isset( $_GET['price_max'] ) || isset( $_GET['type'] ) )
        $args['meta_query'] = array( 'relation'=>'AND' ); // AND significa que todas las condiciones de meta_query deben cumplirse


    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 may 2016 17:20:09
Comentarios

Gracias por tu respuesta, pero tengo mucha curiosidad por hacerlo con la REST API v2.

James Vu James Vu
25 may 2016 17:27:06

Bueno, creo que mi variante es buena, pero si quieres... ¡El hecho es que mi método no está limitado por los parámetros!

Igor Fedorov Igor Fedorov
25 may 2016 17:31:35
0

En WordPress 4.7 se eliminó el argumento filter.

Puedes reactivarlo instalando este plugin proporcionado por el equipo de WordPress. Solo después de eso podrás usar alguna de las soluciones propuestas en las otras respuestas.

Hasta ahora no he encontrado una solución para hacer lo mismo sin instalar el plugin.

12 ene 2017 07:51:54