Filtrare più campi personalizzati con WP REST API 2
Voglio filtrare i post in base a più campi personalizzati ACF con relazione AND. Qualcosa come questo:
$args = array(
'post_type' => 'product', // Tipo di post
'meta_query' => array(
'relation' => 'AND', // Relazione tra i filtri
array(
'key' => 'color', // Campo colore
'value' => 'blue', // Valore blu
'compare' => '=', // Operatore di confronto
),
array(
'key' => 'price', // Campo prezzo
'value' => array( 20, 100 ), // Intervallo di valori
'type' => 'numeric', // Tipo numerico
'compare' => 'BETWEEN', // Operatore between
),
),
);
Potrei avere anche più filtri. Come posso convertirli in filtri per REST API 2?

Questa soluzione funziona con get_items()
nel file /lib/endpoints/class-wp-rest-posts-controller.php
dell'API REST v2 di WordPress
.
Per prima cosa, dovrai costruire gli argomenti GET
come faresti per una new WP_Query()
. Il modo più semplice per farlo è 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 );
Produrrà qualcosa come:
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
Se vuoi renderlo leggibile, puoi anche usare gli strumenti di Chrome e decodeURIComponent('your-query-here')
per semplificare la lettura quando lo inserisci nel tuo URL dell'API REST JSON:
Nota: Per usare il tuo custom post type, dovresti inserire product
prima del ?
/wp-json/wp/v2/<custom-post-type>?filter[meta_query]
Quindi hai la tua query, ma dobbiamo istruire WordPress su come gestire alcune cose:
- Aggiungere il supporto REST per il custom post type
product
- Permettere gli argomenti della query
meta_query
- Analizzare
meta_query
// 1) Aggiungi supporto CPT <product>
function wpse_20160526_add_product_rest_support() {
global $wp_post_types;
//assicurati di impostare questo con il nome del tuo post type!
$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) Aggiungi supporto `meta_query` nella richiesta GET
function wpse_20160526_rest_query_vars( $valid_vars ) {
$valid_vars = array_merge( $valid_vars, array( 'meta_query' ) ); // Ometti meta_key, meta_value se non ti servono
return $valid_vars;
}
add_filter( 'rest_query_vars', 'wpse_20160526_rest_query_vars', PHP_INT_MAX, 1 );
// 3) Analizza Argomenti Personalizzati
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;
}
// sostituisci con gli argomenti della query sanificati
$args['meta_query'] = $meta_query;
}
return $args;
}
add_action( 'rest_product_query', 'wpse_20160526_rest_product_query', 10, 2 );

Ecco un test che ho eseguito in Localhost:
Per motivi di sicurezza, la meta query non è consentita nell'API di WordPress. La prima cosa da fare è aggiungere meta_query alle query REST consentite inserendo questa funzione nel file functions.php
del tuo tema 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' );
Dopo di che, dovrai costruire la query HTML utilizzando questa funzione sull'altro sito web che recupererà i dati dal sito 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);
Ho modificato l'array fields in modo che ora corrisponda ai tuoi argomenti di query. La stringa di query codificata apparirà così:
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
Utilizzando urldecode()
, che in questo caso sarà: urldecode('http://yourwordpreswebssite.com/wp-json/wp/v2/posts?' . $field_string);
, otterrai un URL come questo:
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
Se puoi fornirci l'URL del tuo sito web live, possiamo testarlo direttamente sul tuo sito utilizzando Postman, poiché per testarlo in localhost o su qualsiasi sito WordPress esistente sarà necessario creare un custom post type per i prodotti e aggiungere meta field, ecc. Saluti!

Grazie per la tua risposta ma ho testato con la query come nella domanda su Postman e non ha funzionato.

@Dan ho apportato alcuni miglioramenti alla soluzione, i valori del filtro sono gli stessi degli argomenti della tua query, incluso il custom post type che non era specificato nella soluzione precedente.

Non abbiamo la tassonomia product
. Funziona alla grande! Non avevo pensato di racchiudere meta_query
dentro filter
:)

@Dan Sono contento di sentirlo. Ho scritto un post al riguardo ieri, potresti valutare di condividerlo :) WordPress REST API con campi meta.

Un paio di cose, su alcuni server AWS, usare [] come array può bloccare la richiesta. Dovresti usare semplicemente array() per essere sicuro e per chi potrebbe copiare/incollare. Inoltre, questo supporta il CPT product o solo la tassonomia? E infine, è necessario sanitizzare meta_query? Visto che è stato estratto, corri un rischio di sicurezza accettando qualsiasi cosa fornisca un utente?

@Eduart So come farlo funzionare per me stesso basandomi sulla tua idea ma dovresti modificare anche la risposta.

@jgraup grazie mille per il tuo commento. Non sapevo questo fatto riguardo ai server AWS. I Custom post types e le Custom taxonomy dovrebbero essere registrati con l'API REST prima e sicuramente dovrebbe essere usata la validazione sugli input e l'escape sugli output. Il motivo per cui non ho usato la sanitizzazione è perché l'ho testato solo sull'output per i custom fields che avevo già validato in precedenza sugli input. Ma hai ragione, dovrebbe essere migliorato per altri utenti che si limiteranno a copiare e incollare il codice.

- prima aggiungi il plugin o copia tutto il codice e incollalo nel functions.php da questo link https://github.com/WP-API/rest-filter
- usa questo
?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]='='

Puoi farlo senza Rest API In questo modo (È il mio filtro per i post)
$paged = (get_query_var('paged')) ? get_query_var('paged') : 1;
$args = array(
'paged' => $paged,
'orderby' => 'date', // l'ordinamento per data sarà sempre presente (ma puoi modificarlo/personalizzarlo)
'order' => 'DESC',
);
// creiamo l'array $args['meta_query'] se è specificato almeno un prezzo o è selezionata una checkbox
if( isset( $_GET['price_min'] ) || isset( $_GET['price_max'] ) || isset( $_GET['type'] ) )
$args['meta_query'] = array( 'relation'=>'AND' ); // AND significa che tutte le condizioni meta_query devono essere soddisfatte
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'
);
};

In WordPress 4.7 l'argomento filter
è stato rimosso.
Puoi riattivarlo installando questo plugin fornito dal team di WordPress. Solo dopo potrai utilizzare una delle soluzioni proposte nelle altre risposte.
Non ho ancora trovato una soluzione per fare lo stesso senza installare il plugin.
