Cum să utilizezi REGEXP în cheia meta_query din WP_Query

8 iul. 2015, 00:51:48
Vizualizări: 14.4K
Voturi: 11

Știu că pot folosi REGEXP în WP_Query astfel:

$query = new WP_Query(array(
  'posts_per_page'    => -1,
  'post_status'       => 'publish',
  'meta_query'        => array(
    array(
      'key'     => 'custom_fields',
      'value'   => 'foo[(][0-9][)]', // cu elemente regex
      'compare' => 'REGEXP',
    ),
  ),
));

Dar am nevoie de expresii regulate și în cheie. Ca în acest exemplu:

$query = new WP_Query(array(
  'posts_per_page'    => -1,
  'post_status'       => 'publish',
  'meta_query'        => array(
    array(
      'key'     => 'custom_fields[(][0-9][)]', // cu elemente regex
      'value'   => 'foo',
      'compare' => 'REGEXP',
    ),
  ),
));

Există vreo metodă de a realiza acest lucru folosind un filtru? Sau trebuie să construiesc totul manual cu o interogare SQL personalizată?

0
Toate răspunsurile la întrebare 2
14
10

Iată o idee experimentală:

Să presupunem că avem:

articolul A cu câmpul personalizat location1 ca UK - Londra

articolul B cu câmpul personalizat location2 ca Franța - Paris

articolul C cu câmpul personalizat location3 ca SUA - New York

Apoi am putea folosi, de exemplu:

$args = [
    'meta_query' => [
        'relation' => 'OR',
        [
            'key'          => "^location[0-9]",
            '_key_compare' => 'REGEXP',
            'value'        => 'Londra',
            'compare'      => 'LIKE',
        ],
        [
            'key'          => 'location%',
            '_key_compare' => 'LIKE',
            'value'        => 'Paris',
            'compare'      => 'LIKE'
        ],
        [
            'key'          => 'location3',
            'value'        => 'New York',
            'compare'      => 'LIKE'
        ]
    ]
];

unde suportăm argumentul personalizat _key_compare cu următorul plugin:

<?php
/**
 *  Plugin Name:   Extended Meta Key Search In WP_Query
 *  Description:   Argument personalizat '_key_compare' ca REGEXP, RLIKE sau 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 )
{
    // Verifică interogarea meta:
    $mq = $q->get( 'meta_query' );

    if( empty( $mq ) )
        return;

    // Inițializare:
    $marker = '___tmp_marker___'; 
    $rx     = [];

    // Colectează toate sub-interogările meta care folosesc REGEXP, RLIKE sau LIKE:
    foreach( $mq as $k => $m )                                    
    {
        if(    isset( $m['_key_compare'] )
            && in_array( strtoupper( $m['_key_compare'] ), [ 'REGEXP', 'RLIKE', 'LIKE' ] )
            && isset( $m['key'] )
        ) {
            // Marchează cheia cu un șir unic pentru a securiza înlocuirile ulterioare:
            $m['key'] .= $marker . $k; // Face marcatorul temporar adăugat unic

            // Modifică variabila de interogare originală corespunzătoare:
            $q->query_vars['meta_query'][$k]['key'] = $m['key'];

            // Colectează:
            $rx[$k] = $m;
        }
    }

    // Nimic de făcut:
    if( empty( $rx ) )
        return;

    // Obține acces la SQL generat al interogării meta:
    add_filter( 'get_meta_sql', function( $sql ) use ( $rx, $marker )
    {
        // Rulează o singură dată:
        static $nr = 0;         
        if( 0 != $nr++ )
            return $sql;

        // Modifică partea WHERE unde înlocuim marcatorii temporari:
        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;
    });

});

unde adăugăm marcatori unici pe fiecare cheie meta pentru înlocuirile de șiruri.

Rețineți că acest lucru nu suportă escaparea caracterelor regex, cum ar fi \( și \\.

8 iul. 2015 13:17:40
Comentarii

Îmi plac acești parametri personalizați amuzanți pe care îi adaugi. ;-)

Pieter Goosen Pieter Goosen
8 iul. 2015 13:21:35

Poate ca niște ajutoarele lui Moș Crăciun - de obicei fac totul să funcționeze magic în final ;-) @PieterGoosen

birgire birgire
8 iul. 2015 13:35:13

wow, omule... asta e uimitor. dar din păcate nu funcționează :( Am nume de chei precum custom_field_language(0)language în baza mea de date și expresia mea regulată custom_field_languages[(][0-9][)]language nu funcționează cu plugin-ul tău. Ai vreo idee?

Philipp Kühn Philipp Kühn
8 iul. 2015 17:56:47

se pare că funcția de înlocuire de la finalul plugin-ului tău nu funcționează corect.

Philipp Kühn Philipp Kühn
8 iul. 2015 18:09:32

mersi, am rezolvat asta. Am folosit $count++ în construcția unui șir de caractere când am uitat că trebuia să folosesc $count de două ori ;-) Nu aș recomanda utilizarea de caractere speciale în numele cheilor, în afară de underscore. Tu folosești paranteze în cheia ta. Ele au o semnificație specială în expresiile regulate. Deci ar trebui să le scapi cu \( și \) în REGEXP sau RLIKE, dar escaparea nu este suportată în plugin-ul meu. Ai putea încerca LIKE în schimb cu custom_field_language(%)language. @PhilippKühn

birgire birgire
8 iul. 2015 19:33:56

da. mulțumesc mult. codul tău reparat funcționează!!! dar mai este o problemă. dacă folosesc alt meta_query primesc întotdeauna zero rezultate cu orice relație. ca aici 'meta_query' => array( 'relation' => 'OR', array( 'key' => 'custom_field_location', 'value' => 'Berlin', 'compare' => '=', ), array( 'key' => 'custom_field_languages[(][0-9][)]language', '_key_compare' => 'REGEXP', 'value' => 'ak', 'compare' => 'LIKE', ), )

Philipp Kühn Philipp Kühn
8 iul. 2015 19:56:39

dacă afișez $sql['where'] pot vedea că fiecare cheie este înlocuită cu cea REGEXP. @birgire

Philipp Kühn Philipp Kühn
8 iul. 2015 20:13:45

A fost încă o problemă legată de $count ;-) Te rog să verifici răspunsul actualizat, unde folosesc o cheie de array în schimb. @PhilippKühn

birgire birgire
8 iul. 2015 20:57:06

geniu! funcționează perfect!

Philipp Kühn Philipp Kühn
8 iul. 2015 22:04:22

oh nu... am aflat că acest lucru nu suportă noile interogări meta imbricate introduse în wp 4.1 :( poveste fără sfârșit... @birgire

Philipp Kühn Philipp Kühn
9 iul. 2015 23:53:01

aha, bun punct, poate voi pune asta pe GitHub în săptămânile următoare și voi încerca să o extind acolo ;-) @PhilippKühn

birgire birgire
10 iul. 2015 00:24:55

Asta ar fi minunat! Până atunci am găsit o soluție improvizată. Dar aș fi fericit să folosesc scriptul tău pentru că este o metodă mult mai elegantă. @birgire

Philipp Kühn Philipp Kühn
10 iul. 2015 10:58:10

@birgire te-ai gândit vreodată să postezi asta pe GitHub, prietene?

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

Se pare că am uitat de asta :-) Probabil că nu în viitorul apropiat, dar cine știe...

birgire birgire
31 mai 2023 20:29:04
Arată celelalte 9 comentarii
1

Răspunsul tău funcționează perfect la primul nivel al array-ului, de exemplu:

$args['meta_query'][] = array(

  'key' => 'tour_itinerario_ciudades_repeater_%_tour_ciudades_nombre',
  '_key_compare' => 'LIKE',
  'value' => 'MEXICO',
  'compare' => 'LIKE',
  );

Am nevoie să fac unele modificări pentru a funcționa la al doilea nivel în 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',
    )
);

Acum,

add_action('pre_get_posts', function( $q ) {
// Verifică interogarea meta:
$mq = $q->get('meta_query');

if (empty($mq))
    return;

// Inițializare:
$marker = '___tmp_marker___';
$rx = [];

// Colectează toate sub-interogările meta, care folosesc REGEXP, RLIKE sau LIKE:
// Funcționează doar pentru primul nivel în array
foreach ($mq as $k => $m) {
    if (isset($m['_key_compare']) && in_array(strtoupper($m['_key_compare']), [ 'REGEXP', 'RLIKE', 'LIKE']) && isset($m['key'])
    ) {
        // Marchează cheia cu un șir unic pentru a securiza înlocuirile ulterioare:
        $m['key'] .= $marker . $k; // Face marcatorul temporar adăugat unic
        // Modifică variabila de interogare originală corespunzătoare:
        $q->query_vars['meta_query'][$k]['key'] = $m['key'];

        // Colectează:
        $rx[$k] = $m;
    }
}

// cod personalizat pentru a funcționa cu argumente în array multidimensional 
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'])
            ) {
                // Marchează cheia cu un șir unic pentru a securiza înlocuirile ulterioare:
                $m_i['key'] .= $marker . $k_i; // Face marcatorul temporar adăugat unic
                // Modifică variabila de interogare originală corespunzătoare:
                $q->query_vars['meta_query'][$k][$k_i]['key'] = $m_i['key'];

                // Colectează:
                $rx[$k][$k_i] = $m_i;
            }
        }
    }
}


// Nimic de făcut:
if (empty($rx))
    return;

// Obține acces la SQL generat al interogării meta:
add_filter('get_meta_sql', function( $sql ) use ( $rx, $marker ) {
    // Rulează o singură dată:
    static $nr = 0;
    if (0 != $nr++)
        return $sql;

    // Modifică partea WHERE unde înlocuim marcatorii temporari:
    //PRIMUL NIVEL
    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']
        );
    }
    //AL DOILEA NIVEL
    foreach ($rx as $k => $r) {
        //TODO: testează cu mai multe cazuri deoarece pot exista bug-uri
        if (!isset($r['key'])) {//FORȚEZ INTRAREA 
            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;
});

}); Doar în caz că cineva are nevoie de un răspuns similar,

MULȚUMESC DIN NOU

19 ian. 2016 01:37:10
Comentarii

Mulțumesc pentru partajare, dar ar fi apreciat dacă comentariile din cod ar fi în engleză, mulțumesc.

birgire birgire
19 ian. 2016 16:03:00