¿Cómo puedo crear una meta_query con un array como meta_field?
Aquí están los argumentos para mi consulta:
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => 'sports',
)
)
);
Esto funciona cuando topics
es una cadena de texto, pero no cuando es un array. Me gustaría que esta consulta funcione cuando topics
es por ejemplo array( 'sports', 'nonprofit', etc. )
¿Hay alguna manera de construir meta queries con arrays como meta_key?

Pasando un array de valores posibles a la consulta
Si el valor en la base de datos es una cadena y quieres pasar varios valores a la consulta:
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => array ( 'sports', 'nonprofit', 'community' ),
'compare' => 'IN'
)
)
);
Buscando un valor específico en un array serializado
Si el valor en la base de datos es un array de varios temas y quieres buscar un solo tema dentro de ese array (Nota: un array en la base de datos puede recuperarse como tal, pero se almacena en forma serializada, que también es una cadena):
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => 'sports',
'compare' => 'LIKE'
)
)
);
Usar 'LIKE' como valor de comparación no es una instrucción tan clara como podrías esperar, pero es la mejor opción disponible.
Además de esto, tu única otra opción sería recuperar todos los posts que tengan establecido el meta_key "topics" e iterar sobre ellos manualmente o, en otras palabras, verificar el valor dentro del bucle y mostrar los posts bajo dicha condición.
ACTUALIZACIÓN 2021
Aunque sigue siendo válida, la respuesta anterior tiene 9 años.
Revisa también la excelente respuesta de @sMyle y la de @Kaji.

Siguiendo la respuesta de Johannes, dado que es un array serializado, si estás almacenando algo como IDs de usuario (que fue mi caso), puede que necesites manejarlo de forma un poco diferente.
Los meta datos del post se guardaban así:
array( "1", "23", "99");
Así que sí, son enteros pero a través de update_post_meta
se guardaban como strings.
'meta_query' => array(
array(
'key' => 'my_meta_key',
'value' => serialize( strval( 1 ) ),
'compare' => 'LIKE'
)
)
Por lo que en realidad estás haciendo una comparación LIKE con la versión serializada en string de lo que buscas.
ACTUALIZACIÓN 2022:
Un problema del que hay que estar consciente al hacer esto es que si NO es un string lo que se guarda (se guarda como entero), y usas serialize( intval( $value ) )
, te encuentras con el problema potencial de coincidir con el índice en lugar del valor.
Por ejemplo:
array( 1234, 5678 );
Se guarda así:
a:1:{i:0;i:1234;i:1;i:5678;}
Si usas serialize( intval( 1 ) )
en la meta query, coincidirás con i:1;
que es el índice, NO el valor, así que ten cuidado y si controlas el código, siempre guárdalo como string para que no coincidas con el índice.

Me encontré con esta antigua respuesta por casualidad hoy. Me gusta tu adición. +1

También me encontré con esto, mi situación es que necesito obtener todos los posts donde user_id no esté en el array, pero la solución anterior no funciona así que lo hice así:
'meta_query' => array(
array(
'key' => 'my_meta_key',
'value' => ':' . $user_id . ';',
'compare' => 'NOT LIKE'
)
)
Porque cuando se serializan todos los valores se guardan como: ':valor;'

Inteligente. Me gustan los enfoques ingeniosos, no tan convencionales. No esperaba exactamente encontrar esto aquí esta mañana. Mis respetos.

Otra pequeña mejora basada en la respuesta de @sMyles.
He tenido casos donde los IDs se almacenan tanto como strings (por ejemplo cuando vienen de un formulario) y como enteros (ej. update_post_meta($post_id, authorized_users', array(get_current_user_id()));
). Esto es similar al conocido problema con wp_set_object_terms()
donde puedes usar IDs de términos para establecerlos, pero si no los conviertes a enteros primero, existe un 50% de probabilidad de que crees nuevos términos usando esos números como nombres.
Esto puede resultar en que se almacenen de forma muy diferente en un array serializado, como se ve en estos ejemplos extraídos de la base de datos de mi sitio de prueba:
a:1:{i:0;s:1:"1";} // 's' para 'string', nota las comillas dobles
a:1:{i:0;i:1;} // 'i' para 'integer', sin comillas
Ambos ejemplos, al pasarlos por print_r()
, se mostrarán como:
Array
(
[0] => 1
)
Para solucionar esto, hice un pequeño ajuste al meta_query
añadiendo una relation
y otra versión de la consulta que convierte el valor a entero en vez de string.
Aquí el resultado final:
'meta_query' => array(
'relation' => 'OR', // Indica que cualquiera de las siguientes condiciones es válida
array(
'key' => 'bcm_enm_authorized_users',
'value' => serialize(strval(get_current_user_id())), // Almacenado como string
'compare' => 'LIKE'
),
array(
'key' => 'bcm_enm_authorized_users',
'value' => serialize(intval(get_current_user_id())), // Almacenado como integer
'compare' => 'LIKE'
),
),
EDIT: Me acabo de dar cuenta que este método podría tener riesgo de colisiones con índices de arrays, lo que podría permitir acceso ilícito a materiales si el ID de usuario aparece como índice aunque no esté en el array. Si bien esto funciona para el problema mencionado, la mejor práctica es asegurarse de convertir a string cualquier valor que quieras buscar, para poder usar el método de @sMyles.

Esta debería ser la respuesta seleccionada, la más confiable

Tuve un problema similar hoy. Tenía que consultar un campo de relación de ACF (Advanced Custom Fields) con múltiples usuarios relacionados (array).
Después de actualizar el campo mediante PHP, la consulta no funcionaba. Pero al actualizarlo mediante la interfaz de usuario de ACF, la consulta sí funcionó.
El problema era que mi código PHP establecía los valores de la relación como enteros (int), mientras que la interfaz de usuario los establecía como cadenas (string). Para asegurarme de que ambos funcionen, ahora uso esta consulta (adaptada al ejemplo aquí):
$args = array(
'post_type' => 'news',
'meta_query' => array(
'relation' => 'OR',
array(
'key' => 'topics',
'value' => '1;', // funciona para array de enteros
'compare' => 'LIKE'
),
array(
'key' => 'topics',
'value' => '"1"', // funciona para array de cadenas
'compare' => 'LIKE'
),
)
);

Me inclinaría por la respuesta de Johannes. Sin embargo, quiero mejorarla porque usando ese meta_query, te encontrarás con un caso como este:
tu valor es
array('sports','movies', 'sports2');
cuando buscas
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => 'sports',
'compare' => 'LIKE'
)
)
);
entonces el resultado devolverá tanto 'sports' como 'sports2'.
Para solucionarlo, cambia los argumentos del meta_query a:
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => 'sports";',
'compare' => 'LIKE'
)
)
);
Esto se debe a que el valor está serializado en la base de datos, y cada elemento estará separado por punto y coma. Por lo tanto, los argumentos anteriores funcionarán.
Si los elementos del valor son números, solo necesitas eliminar las comillas dobles "
$args = array(
'post_type' => 'news',
'meta_query' => array(
array(
'key' => 'topics',
'value' => '1;',
'compare' => 'LIKE'
)
)
);
