Usare REGEXP nella chiave meta_query di WP_Query

8 lug 2015, 00:51:48
Visualizzazioni: 14.4K
Voti: 11

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?

0
Tutte le risposte alla domanda 2
14
10

Ecco un'idea sperimentale:

Supponiamo di avere:

articolo A con il campo personalizzato location1 impostato su UK - Londra

articolo B con il campo personalizzato location2 impostato su Francia - Parigi

articolo 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 \\.

8 lug 2015 13:17:40
Commenti

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

Pieter Goosen Pieter Goosen
8 lug 2015 13:21:35

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

birgire birgire
8 lug 2015 13:35:13

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?

Philipp Kühn Philipp Kühn
8 lug 2015 17:56:47

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

Philipp Kühn Philipp Kühn
8 lug 2015 18:09:32

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

birgire birgire
8 lug 2015 19:33:56

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

Philipp Kühn Philipp Kühn
8 lug 2015 19:56:39

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

Philipp Kühn Philipp Kühn
8 lug 2015 20:13:45

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

birgire birgire
8 lug 2015 20:57:06

geniale! funziona perfettamente!

Philipp Kühn Philipp Kühn
8 lug 2015 22:04:22

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

Philipp Kühn Philipp Kühn
9 lug 2015 23:53:01

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

birgire birgire
10 lug 2015 00:24:55

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

Philipp Kühn Philipp Kühn
10 lug 2015 10:58:10

@birgire hai mai pensato di metterlo su GitHub, amico?

Jacques Koekemoer Jacques Koekemoer
30 mag 2023 16:38:28

Sembra che me ne sia dimenticato :-) Probabilmente non nel prossimo futuro, ma non si sa mai...

birgire birgire
31 mag 2023 20:29:04
Mostra i restanti 9 commenti
1

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

19 gen 2016 01:37:10
Commenti

Grazie per la condivisione, ma sarebbe apprezzato se i commenti nel codice fossero in inglese, grazie.

birgire birgire
19 gen 2016 16:03:00