Utilice REGEXP en la clave WP_Query meta_query

10

Sé que puedo usar REGEXP en WP_Query así:

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

Pero también necesito expresiones regulares en la clave. Me gusta esto:

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

¿Hay alguna manera de lograr esto con un filtro tal vez? ¿O tengo que construirlo todo yo mismo con una consulta SQL propia?

Philipp Kühn
fuente

Respuestas:

9

Aquí hay una idea experimental:

Supongamos que tenemos:

publicar A con el campo personalizado location1como Reino Unido - Londres

publicar B con el campo personalizado location2como Francia - París

publicar C con el campo personalizado location3como EE. UU. - Nueva York

Entonces podríamos usar, por ejemplo:

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

donde admitimos el _key_compareargumento personalizado con el siguiente complemento:

<?php
/**
 *  Plugin Name:   Extended Meta Key Search In WP_Query
 *  Description:   Custom '_key_compare' argument as REGEXP, RLIKE or 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 )
{
    // Check the meta query:
    $mq = $q->get( 'meta_query' );

    if( empty( $mq ) )
        return;

    // Init:
    $marker = '___tmp_marker___'; 
    $rx     = [];

    // Collect all the sub meta queries, that use REGEXP, RLIKE or LIKE:
    foreach( $mq as $k => $m )                                    
    {
        if(    isset( $m['_key_compare'] )
            && in_array( strtoupper( $m['_key_compare'] ), [ 'REGEXP', 'RLIKE', 'LIKE' ] )
            && isset( $m['key'] )
        ) {
            // Mark the key with a unique string to secure the later replacements:
            $m['key'] .= $marker . $k; // Make the appended tmp marker unique

            // Modify the corresponding original query variable:
            $q->query_vars['meta_query'][$k]['key'] = $m['key'];

            // Collect it:
            $rx[$k] = $m;
        }
    }

    // Nothing to do:
    if( empty( $rx ) )
        return;

    // Get access the generated SQL of the meta query:
    add_filter( 'get_meta_sql', function( $sql ) use ( $rx, $marker )
    {
        // Only run once:
        static $nr = 0;         
        if( 0 != $nr++ )
            return $sql;

        // Modify WHERE part where we replace the temporary markers:
        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;
    });

});

donde agregamos marcadores únicos en cada meta clave para los reemplazos de cadena.

Tenga en cuenta que esto no admite el escape de caracteres regex , como \(y \\.

Birgire
fuente
Disfruto de estos divertidos parámetros personalizados adicionales que está agregando. ;-)
Pieter Goosen
1
Tal vez como los pequeños ayudantes de Santa: generalmente hacen que todo funcione mágicamente al final ;-) @PieterGoosen
birgire
1
Gracias, arreglé esto. Acabo de usar $count++en una construcción de cadena cuando olvidé que tenía que usar $countdos veces ;-) No recomendaría usar caracteres especiales en los nombres de las teclas, aparte del guión bajo . Estás usando paréntesis en tu clave. Tienen un significado especial con expresiones regulares. Por lo tanto, tendría que escapar de ellos con \(y \)dentro REGEXPo RLIKE, pero escapar no es compatible con mi complemento. Podrías intentarlo LIKEcon custom_field_language(%)language. @ PhilippKühn
birgire
1
¡genio! ¡Funciona de maravilla!
Philipp Kühn
1
aha, buen punto, tal vez solo pondré esto en GitHub en las próximas semanas e intentaré extenderlo allí ;-) @ PhilippKühn
birgire
1

Su respuesta es perfecta trabajando en la primera matriz lvl, por ejemplo:

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

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

Necesito hacer algunas modificaciones para trabajar en el segundo nivel de la matriz:

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

Ahora,

add_action('pre_get_posts', function( $q ) {
// Check the meta query:
$mq = $q->get('meta_query');

if (empty($mq))
    return;

// Init:
$marker = '___tmp_marker___';
$rx = [];

// Collect all the sub meta queries, that use REGEXP, RLIKE or LIKE:
// Only works for 1st level in array
foreach ($mq as $k => $m) {
    if (isset($m['_key_compare']) && in_array(strtoupper($m['_key_compare']), [ 'REGEXP', 'RLIKE', 'LIKE']) && isset($m['key'])
    ) {
        // Mark the key with a unique string to secure the later replacements:
        $m['key'] .= $marker . $k; // Make the appended tmp marker unique
        // Modify the corresponding original query variable:
        $q->query_vars['meta_query'][$k]['key'] = $m['key'];

        // Collect it:
        $rx[$k] = $m;
    }
}

// custom code to make it work with arguments on Multidimensional array 
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'])
            ) {
                // Mark the key with a unique string to secure the later replacements:
                $m_i['key'] .= $marker . $k_i; // Make the appended tmp marker unique
                // Modify the corresponding original query variable:
                $q->query_vars['meta_query'][$k][$k_i]['key'] = $m_i['key'];

                // Collect it:
                $rx[$k][$k_i] = $m_i;
            }
        }
    }
}


// Nothing to do:
if (empty($rx))
    return;

// Get access the generated SQL of the meta query:
add_filter('get_meta_sql', function( $sql ) use ( $rx, $marker ) {
    // Only run once:
    static $nr = 0;
    if (0 != $nr++)
        return $sql;

    // Modify WHERE part where we replace the temporary markers:
    //PRIMER 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']
        );
    }
    //SECOND NIVEL
    foreach ($rx as $k => $r) {
        //TODO: test with several cases since may have bugs
        if (!isset($r['key'])) {//FORZO LA ENTRADA 
            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 por si alguno necesita una respuesta similar,

THK OTRA VEZ

Rogelio Vargas
fuente
Gracias por compartir, pero agradecería que los comentarios del código estuvieran en inglés, gracias.
Birgire