Usando condiciones OR en meta_query para el argumento query_posts
He buscado por todas partes pero no puedo encontrar nada.
Estoy tratando de filtrar algunos resultados y cuando creo un array de argumentos como este y lo paso a query_posts, noté que el SQL generado usa AND en lugar de OR para el valor 'class_1_days'. Me gustaría convertir eso en OR. ¿Cómo podría hacer esto? (Estoy usando LIKE ya que los datos están serializados, de lo contrario habría intentado usar "IN" y pasar un array. ¿Cuál es la forma correcta de hacer esto?
$args = array(
'taxonomy' => 'courses',
'term' => 'dibujo',
'post_type' => 'school',
'paged' => $paged,
'meta_query' =>
#$checkbox_array,
array(
array(
'key' => 'instructor',
'value' => '1128',
'compare' => 'LIKE',
),
array(
'key' => 'class_1_days',
'value' => 'Viernes',
'compare' => 'LIKE',
),
array(
'key' => 'class_1_days',
'value' => 'Sábado',
'compare' => 'LIKE',
),
)
);
Este es el SQL relevante que se está generando donde noté el AND
[138] => Array
(
[0] => SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts INNER JOIN wp_term_relationships ON (wp_posts.ID = wp_term_relationships.object_id) INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
INNER JOIN wp_postmeta AS mt2 ON (wp_posts.ID = mt2.post_id) WHERE 1=1 AND ( wp_term_relationships.term_taxonomy_id IN (18) ) AND wp_posts.post_type = 'school' AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private') AND ( (wp_postmeta.meta_key = 'instructor' AND CAST(wp_postmeta.meta_value AS CHAR) LIKE '%1128%')
AND (mt1.meta_key = 'class_1_days' AND CAST(mt1.meta_value AS CHAR) LIKE '%Friday%')
AND (mt2.meta_key = 'class_1_days' AND CAST(mt2.meta_value AS CHAR) LIKE '%Saturday%') ) GROUP BY wp_posts.ID ORDER BY wp_posts.post_date DESC LIMIT 0, 5

Usa 'relation' => 'OR'
como en el ejemplo del Codex a continuación:
$args = array(
'post_type' => 'product',
'meta_query' => array(
'relation' => 'OR', /* <-- aquí */
array(
'key' => 'color',
'value' => 'blue',
'compare' => 'NOT LIKE'
),
array(
'key' => 'price',
'value' => array( 20, 100 ),
'type' => 'numeric',
'compare' => 'BETWEEN'
)
)
);
$query = new WP_Query( $args );
Eso te dará un OR
entre todos tus bloques de meta_query. Si lo que necesitas es algo como WHERE "instructor" = 1128 AND (class_1_days = "value1" OR class_1_days = "value2")
, entonces algo como esto funcionaría si tus datos no estuvieran serializados.
$args = array(
'taxonomy' => 'courses',
'term' => 'drawing',
'post_type' => 'school',
'paged' => $paged,
'meta_query' =>
array(
array(
'key' => 'instructor',
'value' => '1128',
'compare' => 'LIKE',
),
array(
'key' => 'class_1_days',
'value' => array('Friday','Saturday')
'compare' => 'IN',
)
)
);
Dado que tienes datos serializados, esto va a ser difícil. Podrías escribir un filtro para posts_where
, pero esperaría que el resultado fuera ineficiente y propenso a errores.
El enfoque correcto para casos como este, donde necesitas buscar/filtrar/ordenar por algún fragmento de información, es, en mi experiencia, no almacenar tus datos como un array serializado en primer lugar. Ese es simplemente el enfoque incorrecto si necesitas consultas como esta.
Si estuviera tomando este proyecto, recorrería los datos que necesitan ser consultados de esta manera y volvería a guardar cada parte como un par clave/valor. Las partes que no estás consultando pueden permanecer serializadas. No hay una manera (buena) de hacer esto en MySQL con funciones de MySQL, ya sea como parte de la consulta que estás intentando o como alguna consulta independiente. He visto intentos de crear una función MySQL "unserialize", pero no le veo el sentido. Volver a guardar los datos es el camino a seguir.
Debería ser una consulta bastante simple obtener todos los datos de class_1_days
. Luego iterar sobre ellos y guardar las partes que necesitas consultar como claves independientes y volver a serializar el resto.

Muchas gracias por tu respuesta. ¿Hay alguna manera de hacer un AND u OR? Estoy intentando mantener la parte del instructor. Tu ejemplo los convierte todos en OR. Solo necesito que la parte de class_1_days sea un OR. Algo similar a si lo tuviera como 'value' => array(bla,bla,bla), 'compare' => IN
Básicamente donde Instructor = 'bla' AND value IN (bla,bla,bla)

Gracias @S_ha_dam. ¿Hay alguna forma de deserializar la base de datos? Esto es algo que heredé de segunda mano, así que no sé qué tan afectará. Vi un enlace a una aplicación que hacía exactamente eso, pero el enlace estaba roto.

Gracias, sin embargo simplemente volver a guardar no funcionará porque esto es un entorno en producción donde los datos se guardan constantemente y class_1_data es solo parte del problema. Hay múltiples como class_0_data, class_2_data, etcétera, y esos son añadidos por los usuarios. Creo que el mejor enfoque puede ser retroceder y encontrar una manera de reinstalar el módulo y hacer que no guarde serializado (si descubro cómo) y partir de ahí X-( ... No mencioné la parte de las otras clases porque pensé que podría resolverlo por mi cuenta

Definitivamente tendrás que refactorizar la función "save", pero también necesitarás volver a guardar los datos existentes o terminarás con una mezcla extraña de datos serializados y no serializados. Haz que el código funcione en un servidor de desarrollo y luego pon el sitio en vivo en modo mantenimiento durante veinte minutos en un horario de bajo tráfico.

¡Oh sí, definitivamente tendré que cambiar los datos actuales!
Esto apesta. ¡Pensé que sería una solución fácil, ugh!

Espera... ¿qué tal esto... hay alguna manera de agregar OTRO filtro DESPUÉS de este filtro que sea por instructor?
¿O incluso recuperar el array php directo que WordPress está usando y editarlo?

No estoy seguro de entender la última pregunta, pero mediante filtros puedes modificar la consulta para que sea prácticamente lo que desees. Sin embargo, al "agregar" algo, dudo que termines con una consulta eficiente.

Eh, digamos que en PHP tendrías tu array real como classes[1][bla], classes[2][bla], etc. Si supiera cómo se llama ese array, potencialmente podría hacer alguna búsqueda en arrays de PHP y expresiones regulares para filtrarlo por mi cuenta después de obtener los resultados deseados de la declaración OR que proporcionaste anteriormente.

Según el codex de WordPress, se pueden usar arrays anidados para construir consultas complejas. En tu ejemplo necesitas algo como esto:
$args = array(
'taxonomy' => 'courses',
'term' => 'drawing',
'post_type' => 'school',
'paged' => $paged,
'meta_query' =>
array(
'relation' => 'AND', // relación por defecto
array(
'key' => 'instructor',
'value' => '1128',
'compare' => 'LIKE',
),
array(
'relation' => 'OR',
array(
'key' => 'class_1_days',
'value' => 'Friday',
'compare' => 'LIKE',
),
array(
'key' => 'class_1_days',
'value' => 'Saturday',
'compare' => 'LIKE',
),
)
)
);

Tuve que investigar para encontrar esto. Tengo una consulta compleja relacionada con este tema. Has recibido tantos votos positivos que creo que voy a hacer una nueva pregunta basada en esto. Honestamente, estoy muy interesado en ver cómo la comunidad está abordando este tipo de problema actualmente.
