Búsqueda compleja de custom_field con meta_query

5 minutos de lectura

avatar de usuario
trex005

Fondo

Estoy usando Advanced Custom Fields Pro para administrar mis campos personalizados y tienen un campo “repetidor” que contiene subcampos que se almacenan como repeatername_X_fieldname donde X es el número de fila del repetidor.

Tengo un tipo de publicación personalizada. student que tiene el repetidor attendance que contiene date y class.

Entonces, cuando un estudiante asiste a una clase, almacenará su asistencia de la siguiente manera

  • meta_clave:’asistencia_X_fecha’ meta_valor:’20170701′
  • meta_clave:’asistencia_X_clase’ meta_valor:’Historia 101′

Para buscar a cualquier estudiante que haya estado en una determinada clase o haya asistido dentro de un determinado rango de fechas, tengo que conectarme a get_meta_sql y convertir mi meta_query usar LIKE en vez de = cuando el valor contiene %

function key_rewrite($parts){
    foreach($parts as &$part){
        $part = preg_replace("/(meta_key = )(\'[^']*[%][^']*\')/", "meta_key LIKE $2", $part);
    }
    return $parts;
}
add_action( 'get_meta_sql', 'key_rewrite');

Esto me permite hacer algo como

$args = array(
    'post_type' => 'student',
    'meta_query' => array(
        array(
            'key'=>'attendance_%_class',
            'compare'=>'=',
            'value'=>'History 101'
        )
    )
);
$my_query = new WP_Query($args);

para buscar a alguien que haya asistido a Historia 101 O

$args = array(
    'post_type' => 'student',
    'meta_query' => array(
        array(
            'key'=>'attendance_%_date',
            'compare'=>'>=',
            'value'=>'20170101'
        )
    )
);

Para buscar a cualquiera que haya asistido este año.

Problema parte 1

Necesito poder buscar a alguien que haya asistido a ‘Historia 101’ este año.

Inicialmente, podría parecer que un simple AND en meta_query haría el truco:

$args = array(
    'post_type' => 'student',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'key'=>'attendance_%_class',
            'compare'=>'=',
            'value'=>'History 101'
        ),
        array(
            'key'=>'attendance_%_date',
            'compare'=>'>=',
            'value'=>'20170101'
        )
    )
);

Sin embargo, debido a que los comodines no están vinculados, esto podría devolver a alguien que asistió a ‘Historia 101’ el año pasado, pero a una clase diferente este año.

Problema parte 2

yo Realmente necesita poder obtener una lista de todos los que han asistido a ‘Historia 101’ este año pero que no se han presentado a la clase en absoluto la semana pasada. Esto complica aún más el problema porque necesito combinar meta_query EXISTS y NOT EXISTS con una condición adicional. Nuevamente, en la superficie, esto suena bastante simple usando meta_consultas anidadas:

$args = array(
    'post_type' => 'student',
    'meta_query' => array(
        'relation' => 'AND',
        array(
            'relation' => 'AND',
            array(
                //Just assume by some magic we resolved Problem part 1
                'key'=>'attendance_%_class',
                'compare'=>'=',
                'value'=>'History 101'
            ),
            array(
                'key'=>'attendance_%_date',
                'compare'=>'>=',
                'value'=>'20170101'
            )
        ),
        array(
            'relation' => 'AND',
            array(
                //again... magic!
                'key'=>'attendance_%_class',
                'compare'=>'=',
                'value'=>'History 101'
            ),
            array(
                'key'=>'attendance_%_date',
                'compare'=>'>',
                'value'=>'20170821'
            ),
            array(
                'key'=>'attendance_%_date',
                'compare'=>'NOT EXISTS'
            )
        )
    )
);

Obviamente, esto se debe a problemas lógicos, pero de manera impresionante WordPress resuelve la mayoría de ellos uniéndose a la tabla postmeta una vez por uso en la consulta meta. Desafortunadamente, eso significa que la parte de la fecha > no se usa en el NOT EXISTS ON y por lo tanto no puede usar el IS NULL para probar que no existe.

Entiendo que esto fue muy complejo y si me seguiste, estoy completamente impresionado. Si no, por favor haga preguntas para que pueda ayudar a aclarar.

Sí, soy consciente de que podría escribir completamente mi propia consulta, pero estoy tratando de apegarme a las herramientas integradas de WordPress.

¡AYUDA!

avatar de usuario
Plamen Nikolov

Estuve allí… tratando de poner todo en una metaconsulta compleja. Al final, debe procesar manualmente el SQL generado: mover corchetes, reemplazar operadores, comillas, etc.

Al final, la consulta es tan compleja y tiene múltiples JOINS al postmeta mesa se vuelve demasiado caro y lento.

Elegí lograr esto eligiendo un enfoque ligeramente diferente. Entonces, lo que puede hacer es dividir la consulta en varias subconsultas y combinarlas más tarde con post__in y post__not_in,

Por ejemplo para Problema parte 1:

/* Filter by class */
$history_students_ids = get_posts(array(
    'post_type'      => 'student',
    'fields'         => 'ids',
    'posts_per_page' => -1,
    'meta_query'     => array(
        array(
            'key'=>'attendance_%_class',
            'compare'=>'=',
            'value'=>'History 101'
        ),
    )
));

/* Filter by date */
$students = get_posts(array(
    'post_type' => 'student',
    'post__in' => $history_students_ids,
    'meta_query' => array(
        array(
            'key'=>'attendance_%_date',
            'compare'=>'>=',
            'value'=>'20170101'
        )
    )
));

Lo mismo va para Problema parte 2

$not_attended_history_students_ids = get_posts(array(
    'post_type' => 'student',
    'post__in' => $history_students_ids,
    'fields'         => 'ids',
    'posts_per_page' => -1,
    'meta_query' => array(
        array(
            'key'=>'attendance_%_date',
            'compare'=>'NOT EXISTS'
        )
    )
));

$students = get_posts(array(
    'post_type' => 'student',
    'post__in' => $not_attended_history_students_ids,
    'meta_query' => array(
        array(
            'key'=>'attendance_%_date',
            'compare'=>'>=',
            'value'=>'20170101'
        )
    )
)); 

Puede revertir la condición de asistencia para que EXISTA y usar post__not_in… Espero que hayas logrado entender la idea.

  • A primera vista, esto parece una gran solución. En cuanto tenga la oportunidad lo pruebo y te comento.

    – trex005

    29 de agosto de 2017 a las 18:23

  • Bueno. Esto funciona muy bien para la Parte 2. Sin embargo, la Parte 1 todavía tiene el mismo problema. Por ejemplo, si el “Estudiante 1” asistió a “Historia 101” el año pasado, estaría en el primer get_posts resultado. Entonces, si asistiera a “Historia 102” este año, estaría en el segundo get_posts.

    – trex005

    29 de agosto de 2017 a las 18:38

  • intente Wp_query post__not_in (codex.wordpress.org/Class_Reference/WP_Query) en el segundo ciclo

    – Gnanasekaran Loganathan

    1 de septiembre de 2017 a las 10:48


¿Ha sido útil esta solución?