Usare REGEXP nella chiave meta_query di WP_Query
So che posso usare REGEXP in WP_Query in questo modo:
$query = new WP_Query(array(
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'custom_fields',
'value' => 'foo[(][0-9][)]', // con roba regex
'compare' => 'REGEXP',
),
),
));
Ma ho bisogno di usare espressioni regolari anche nella chiave. In questo modo:
$query = new WP_Query(array(
'posts_per_page' => -1,
'post_status' => 'publish',
'meta_query' => array(
array(
'key' => 'custom_fields[(][0-9][)]', // con roba regex
'value' => 'foo',
'compare' => 'REGEXP',
),
),
));
C'è un modo per ottenere questo risultato magari con un filtro? O devo costruire tutto da solo con una query SQL personalizzata?

Ecco un'idea sperimentale:
Supponiamo di avere:
articolo A con il campo personalizzato
location1
impostato su UK - Londraarticolo B con il campo personalizzato
location2
impostato su Francia - Parigiarticolo C con il campo personalizzato
location3
impostato su USA - New York
Potremmo quindi utilizzare, ad esempio:
$args = [
'meta_query' => [
'relation' => 'OR',
[
'key' => "^location[0-9]",
'_key_compare' => 'REGEXP',
'value' => 'Londra',
'compare' => 'LIKE',
],
[
'key' => 'location%',
'_key_compare' => 'LIKE',
'value' => 'Parigi',
'compare' => 'LIKE'
],
[
'key' => 'location3',
'value' => 'New York',
'compare' => 'LIKE'
]
]
];
dove supportiamo l'argomento personalizzato _key_compare
con il seguente plugin:
<?php
/**
* Plugin Name: Ricerca Estesa delle Meta Key in WP_Query
* Description: Argomento personalizzato '_key_compare' con REGEXP, RLIKE o LIKE
* Plugin URI: http://wordpress.stackexchange.com/a/193841/26350
* Plugin Author: Birgir Erlendsson (birgire)
* Version: 0.0.3
*/
add_action( 'pre_get_posts', function( $q )
{
// Controlla la meta query:
$mq = $q->get( 'meta_query' );
if( empty( $mq ) )
return;
// Inizializzazione:
$marker = '___tmp_marker___';
$rx = [];
// Raccogli tutte le sotto meta query che utilizzano REGEXP, RLIKE o LIKE:
foreach( $mq as $k => $m )
{
if( isset( $m['_key_compare'] )
&& in_array( strtoupper( $m['_key_compare'] ), [ 'REGEXP', 'RLIKE', 'LIKE' ] )
&& isset( $m['key'] )
) {
// Marca la key con una stringa unica per garantire le sostituzioni successive:
$m['key'] .= $marker . $k; // Rende il marker temporaneo univoco
// Modifica la corrispondente variabile di query originale:
$q->query_vars['meta_query'][$k]['key'] = $m['key'];
// Raccolgila:
$rx[$k] = $m;
}
}
// Nulla da fare:
if( empty( $rx ) )
return;
// Ottieni l'accesso all'SQL generato dalla meta query:
add_filter( 'get_meta_sql', function( $sql ) use ( $rx, $marker )
{
// Esegui solo una volta:
static $nr = 0;
if( 0 != $nr++ )
return $sql;
// Modifica la parte WHERE dove sostituiamo i marker temporanei:
foreach( $rx as $k => $r )
{
$sql['where'] = str_replace(
sprintf(
".meta_key = '%s' ",
$r['key']
),
sprintf(
".meta_key %s '%s' ",
$r['_key_compare'],
str_replace(
$marker . $k,
'',
$r['key']
)
),
$sql['where']
);
}
return $sql;
});
});
dove aggiungiamo marker univoci su ogni meta key per le sostituzioni di stringhe.
Nota che questo non supporta l'escaping dei caratteri regex, come \(
e \\
.

Mi piacciono questi divertenti parametri personalizzati aggiuntivi che stai aggiungendo. ;-)

Forse come i piccoli aiutanti di Babbo Natale - di solito alla fine fanno funzionare tutto magicamente ;-) @PieterGoosen

wow amico... questo è incredibile. ma sfortunatamente non funziona :( Ho nomi di chiave come custom_field_language(0)language
nel mio database e la mia chiave regex custom_field_languages[(][0-9][)]language
non funziona con il tuo plugin. qualche idea?

sembra che la funzione replace alla fine del tuo plugin non funzioni correttamente.

grazie, ho risolto. Ho usato $count++
nella costruzione di una stringa quando mi sono dimenticato che dovevo usare $count
due volte ;-) Non consiglierei di usare caratteri speciali nei nomi delle chiavi, a parte l'underscore. Stai usando parentesi nella tua chiave. Hanno un significato speciale con le espressioni regolari. Quindi dovresti fare l'escape con \(
e \)
in REGEXP
o RLIKE
, ma l'escape non è supportato nel mio plugin. Potresti provare con LIKE
invece con custom_field_language(%)language
. @PhilippKühn

si. grazie mille. il tuo codice modificato funziona!!! ma c'è un altro problema. se uso un altro meta_query ottengo sempre zero risultati con qualsiasi relazione. come questo 'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'custom_field_location',
'value' => 'Berlino',
'compare' => '=',
),
array(
'key' => 'custom_field_languages[(][0-9][)]language',
'_key_compare' => 'REGEXP',
'value' => 'ak',
'compare' => 'LIKE',
),
)

se stampo $sql['where']
posso vedere che ogni chiave viene sostituita con quella REGEXP. @birgire

Questo era un altro problema con $count
;-) Per favore controlla la risposta aggiornata, dove uso una chiave di array invece. @PhilippKühn

oh no... ho scoperto che questo non supporta le nuove query meta annidate introdotte in wp 4.1 :( una storia senza fine... @birgire

aha, buon punto, forse lo metterò su GitHub nelle prossime settimane e cercherò di estenderlo lì ;-) @PhilippKühn

Sarebbe fantastico! Fino ad allora ho trovato una soluzione alternativa un po' rozza. Ma sarei felice di usare il tuo script perché è un modo molto più pulito. @birgire

La tua risposta funziona perfettamente per il primo livello dell'array, ad esempio:
$args['meta_query'][] = array(
'key' => 'tour_itinerario_ciudades_repeater_%_tour_ciudades_nombre',
'_key_compare' => 'LIKE',
'value' => 'MEXICO',
'compare' => 'LIKE',
);
Ho bisogno di fare alcune modifiche per farlo funzionare al secondo livello dell'array:
$args['meta_query'][] = array(
'relation' => 'OR',
array(
'key' => 'tour_itinerario_ciudades_repeater_%_tour_ciudades_nombre',
'_key_compare' => 'LIKE',
'value' => 'CONDESA',
'compare' => 'LIKE',
),
array(
'key' => 'tour_itinerario_ciudades_repeater_%_tour_ciudades_nombre',
'_key_compare' => 'LIKE',
'value' => 'Ciudad 1',
'compare' => 'LIKE',
)
);
Ora,
add_action('pre_get_posts', function( $q ) {
// Controlla la meta query:
$mq = $q->get('meta_query');
if (empty($mq))
return;
// Inizializza:
$marker = '___tmp_marker___';
$rx = [];
// Raccoglie tutte le sotto meta query che usano REGEXP, RLIKE o LIKE:
// Funziona solo per il primo livello dell'array
foreach ($mq as $k => $m) {
if (isset($m['_key_compare']) && in_array(strtoupper($m['_key_compare']), [ 'REGEXP', 'RLIKE', 'LIKE']) && isset($m['key'])
) {
// Marca la chiave con una stringa unica per garantire le sostituzioni successive:
$m['key'] .= $marker . $k; // Rende il marker temporaneo univoco
// Modifica la corrispondente variabile di query originale:
$q->query_vars['meta_query'][$k]['key'] = $m['key'];
// La raccoglie:
$rx[$k] = $m;
}
}
// Codice personalizzato per farlo funzionare con argomenti su array multidimensionali
foreach ($mq as $k => $m) {
foreach ($m as $k_i => $m_i) {
if (count($m_i) >= 3) {
if (isset($m_i['_key_compare']) && in_array(strtoupper($m_i['_key_compare']), [ 'REGEXP', 'RLIKE', 'LIKE']) && isset($m_i['key'])
) {
// Marca la chiave con una stringa unica:
$m_i['key'] .= $marker . $k_i; // Rende il marker temporaneo univoco
// Modifica la variabile di query originale:
$q->query_vars['meta_query'][$k][$k_i]['key'] = $m_i['key'];
// La raccoglie:
$rx[$k][$k_i] = $m_i;
}
}
}
}
// Niente da fare:
if (empty($rx))
return;
// Ottieni accesso all'SQL generato della meta query:
add_filter('get_meta_sql', function( $sql ) use ( $rx, $marker ) {
// Esegui solo una volta:
static $nr = 0;
if (0 != $nr++)
return $sql;
// Modifica la parte WHERE dove sostituiamo i marker temporanei:
//PRIMO LIVELLO
foreach ($rx as $k => $r) {
$sql['where'] = str_replace(
sprintf(
".meta_key = '%s' ", $r['key']
), sprintf(
".meta_key %s '%s' ", $r['_key_compare'], str_replace(
$marker . $k, '', $r['key']
)
), $sql['where']
);
}
//SECONDO LIVELLO
foreach ($rx as $k => $r) {
//TODO: testare con diversi casi poiché potrebbe avere bug
if (!isset($r['key'])) {//FORZO L'INGRESSO
foreach ($r as $k_i => $r_i) {
$sql['where'] = str_replace(
sprintf(
".meta_key = '%s' ", $r_i['key']
), sprintf(
".meta_key %s '%s' ", $r_i['_key_compare'], str_replace(
$marker . $k_i, '', $r_i['key']
)
), $sql['where']
);
}
}
}
var_dump($sql);
return $sql;
});
}); Solo nel caso qualcuno abbia bisogno di una risposta simile,
GRAZIE ANCORA
