Cómo actualizar el valor de update_post_meta como array
Creé un nuevo campo personalizado dentro de mis elementos de menú de navegación como opciones de selección múltiple usando update_post_meta()
como se muestra a continuación
function YPE_update_custom_fields($menu_id, $menu_item_db_id, $menu_item_data) {
if (is_array($_REQUEST['menu-item-content-multiple'])) {
update_post_meta($menu_item_db_id, '_menu_item_content_multiple', $_REQUEST['menu-item-content-multiple'][$menu_item_db_id]);
}
}
add_action('wp_update_nav_menu_item', 'YPE_update_custom_fields', 10, 3);
function YPE_setup_custom_fields($item) {
$item->content_multiple = get_post_meta($item->ID, '_menu_item_content_multiple', true);
return $item;
}
add_filter('wp_setup_nav_menu_item', 'YPE_setup_custom_fields');
Luego agregué un nuevo campo en el archivo Walker_Nav_Menu_Edit
como se muestra a continuación
EDITADO
<?php
$select_options = array (
'key_1' => 'opción1',
'key_2' => 'opción2',
'key_3' => 'opción3'
);
?>
<p class="field-content-multiple description description-thin">
<label for="edit-menu-item-content-multiple-<?php echo $item_id; ?>">
<?php _e( 'Contenido Múltiple' ); ?><br />
<select name="menu-item-content-multiple[<?php echo $item_id; ?>][]" id="edit-menu-item-content-multiple-<?php echo $item_id; ?>" class="widefat code edit-menu-item-content-multiple" multiple="multiple">
<?php foreach ($select_options as $key => $value): ?>
<option value="<?php echo $key; ?>" <?php echo selected(in_array($key, $item->content_multiple)); ?>><?php echo $value;?></option>
<?php endforeach ?>
</select>
</label>
</p>
Mi código funciona con el cuadro de selección única sin ningún problema, pero cuando uso in_array()
en casos de selección múltiple devuelve este error
Warning: in_array() expects parameter 2 to be array, null given

Tú haces esto:
$item->content_multiple = get_post_meta($item->ID, '_menu_item_content_multiple', true);
Observa cómo estableces el tercer parámetro de get_post_meta()
como true
, lo que significa que solo se devolverá un valor. Si lees la documentación, verás que, si el tercer parámetro es false
, obtendrás un array con todos los valores para ese campo meta.
Recuerda que los campos meta pueden ser únicos o múltiples. O pueden ser únicos con una cadena de datos serializada.
Tal como está, tu código parece almacenar todos los valores del select múltiple en un solo campo meta. Entonces, obtienes un array en la solicitud y lo pasas como un único valor del campo meta. Cuando el valor de un campo meta es un array, WordPress lo convierte en una cadena serializada antes de almacenarlo en la base de datos; esto se hace internamente con la función maybe_serialize()
. Luego, cuando intentas obtener el valor del campo meta, si es una cadena serializada, WordPress la pasa por maybe_unserialize()
, así que la cadena serializada se convierte de nuevo al array:
Vamos a explicarlo con un ejemplo genérico.
Este será el array de valores a almacenar en la base de datos:
$valores = [ 'rojo', 'amarillo', 'azul', 'rosa' ];
Si tu clave meta es "color", entonces tienes dos opciones:
Múltiples entradas para la misma clave meta
$valores = [ 'rojo', 'amarillo', 'azul', 'rosa' ];
foreach( $valores as $valor ) {
// Este método usa `add_post_meta()` en lugar de `update_post_meta()`
add_post_meta( $item_id, 'color', $valor );
}
Ahora, el elemento identificado con $item_id
tendrá varias entradas para la clave meta color
. Luego, puedes usar get_post_meta()
con el tercer parámetro establecido como false
y obtendrás un array con todos los valores:
// Realmente no necesitas establecer el tercer parámetro
// porque es false por defecto
$colores = get_post_meta( $item_id, 'color' );
// $colores debería ser un array con todos los valores meta para la clave meta color
var_dump( $colores );
Almacenar el array en una única entrada meta
En este caso, el array de valores se serializa antes de almacenarse en la base de datos:
$valores = [ 'rojo', 'amarillo', 'azul', 'rosa' ];
// WordPress hace esto automáticamente cuando se pasa un array como valor meta
// $valores = maybe_serialize( $valores );
update_post_meta( $item_id, 'color', $valores );
Ahora, el elemento identificado con $item_id
tendrá solo una entrada para la clave meta color
; el valor es una cadena serializada que representa el array original. Luego, puedes usar get_post_meta()
, con el tercer parámetro establecido como true, ya que solo tienes una entrada, y luego deserializar la cadena para obtener el array de nuevo:
$colores = get_post_meta( $item_id, 'color', true );
// WordPress hace esto automáticamente cuando el valor meta es un array serializado
// $colores = maybe_unserialize( $colores );
// $colores debería ser un array
var_dump( $colores );
Puedes seguir el mismo enfoque con el select múltiple de tu formulario.
Con solo una entrada para la clave meta:
if( ! empty( $_REQUEST['menu-item-content-multiple'][$menu_item_db_id] ) ) {
$valor_campo_meta = $_REQUEST['menu-item-content-multiple'][$menu_item_db_id];
// $valor_campo_meta será serializado automáticamente por WordPress
update_post_meta( $menu_item_db_id, '_menu_item_content_multiple', $valor_campo_meta );
}
Luego, puedes usar el valor como array:
// El valor devuelto por get_post_meta() es deserializado automáticamente por WordPress
$item->content_multiple = get_post_meta( $item->ID, '_menu_item_content_multiple', true );
Con múltiples entradas:
El concepto aquí es un poco diferente; tendrás varias entradas en la base de datos con la misma clave meta, por lo que necesitas usar add_post_meta()
en lugar de update_post_meta()
para añadir una nueva entrada por cada valor.
if( ! empty( $_REQUEST['menu-item-content-multiple'][$menu_item_db_id] ) ) {
$valores = $_REQUEST[ 'menu-item-content-multiple' . $menu_item_db_id ];
foreach( $valores as $valor ) {
add_post_meta( $menu_item_db_id, '_menu_item_content_multiple', $valor );
}
}
Ahora, puedes usar get_post_meta()
con el tercer parámetro establecido como false
(es el valor por defecto, así que puedes omitirlo):
$item->content_multiple = get_post_meta( $item->ID, '_menu_item_content_multiple', false );
Ambas opciones son correctas, debes decidir cuál es mejor para organizar los datos dentro de tu proyecto.
Nota adicional: deberías hacer alguna sanitización antes de usar los datos de entrada, pero no conozco los requisitos para ti, aquí un ejemplo:
array_map( 'sanitize_text_field', wp_unslash( $_REQUEST['menu-item-content-multiple'][$menu_item_db_id] ) );

Gracias, ¿debo usar false en lugar de true? Pero también usaste true en get_post_meta !!!

Según está tu código, no, debes usar el método serialize y unserialize. Para poder usar false
en get_post_meta()
deberías también guardar cada valor de tu select
en una entrada separada en la base de datos, con la misma clave pero múltiples veces, es decir, usar update_post_meta()
para cada valor pero siempre con la misma clave; entonces podrías usar false
en get_post_meta()
para obtener todos ellos. Espero que entiendas lo que quiero decir.

Gracias, tu publicación resolvió el mensaje de error, edité mi publicación y agregué un cuadro de selección de campo personalizado como en mi archivo. Pero el problema es que cuando selecciono múltiples opciones después de guardar la configuración, solo se guarda la última opción y no todas las opciones seleccionadas. ¿Por qué? ¿Hay algún error al usar la función selected() de WordPress? <?php echo selected(in_array($key, $item->content_multiple)); ?> (usé tu segunda opción de código)

Ten en cuenta que para usar múltiples entradas para la misma clave meta, debes ejecutar update_post_meta()
para cada valor, pero usando la misma clave cada vez, como hice en la respuesta.

Sí, quiero múltiples entradas para la misma clave meta, pero no sé cómo usar este método. Por favor ayúdame y explícame esto con código

Continuemos esta discusión en el chat.

Lo siento cybmeta, pero tengo otro problema con deseleccionar opciones. Cuando quiero deseleccionar una opción ya seleccionada, después de guardar la configuración permanece seleccionada y no puedo deseleccionar ninguna opción después de la selección.

Simplemente usa delete_post_meta()
cuando lo necesites. He añadido un ejemplo básico.

Gracias, funciona pero no puedo eliminar todas las opciones seleccionadas y también permanece la primera opción seleccionada. El delete_post_meta
no elimina la primera opción seleccionada.

WordPress serializa/deserializa automáticamente los datos de arrays en un solo campo meta (al igual que las funciones de opciones), no deberías serializar/deserializar manualmente.

@Milo por qué el delete_post_meta
en esta respuesta no deselecciona o elimina la primera opción seleccionada.

@Milo, buen ojo, estaba confundido con la explicación en el codex y en el portal de desarrolladores, pero al ver el código fuente, puedo confirmar que WordPress automáticamente hace el serialize/unserialize por nosotros; respuesta actualizada.

No estás usando la función selected()
correctamente. selected()
necesita 1 parámetro obligatorio y 2 opcionales. En tu caso, es mejor usar $selected y $current.
Te falta el guión bajo antes del nombre de tu meta-campo en el nombre del select.
Para selected():
selected( $selected, $current, $echo);
Puedes leer más sobre esto en la referencia del codex referencia de función selected()
Tu select debe ser así:
<select name="_menu-item-content-multiple[]" multiple>
<?php
foreach ($select_options as $key => $value) {
echo '<option value="'.$key.'" '.selected(in_array($key, $item->content_multiple), $key).'>'.$value.'</option>';
}
?>
</select>

Usé tu código pero también muestra el error, creo que el error está en $item->content_multiple, debe guardarse como array porque $item->content_multiple también guarda un solo valor y no guarda más de un valor como array

Ok, verifica que el nombre de tu meta-field sea siempre el mismo, actualmente no lo es. Si quieres que tenga un prefijo de guión bajo, siempre usa este guión bajo, y nombra tu select con guión bajo o con guión. update_post_meta funciona pero no se guardará ningún valor.

Agregué más de 10 campos como el campo de arriba, todos funcionan sin ningún problema. Digo que el error está en $item->content_multiple porque este campo también guarda un valor único y no guarda más de un valor al mismo tiempo porque no se actualiza como array. Antes del bucle foreach, cuando pongo $item->content_multiple = array(); funciona y no me muestra ningún error pero no guarda los valores

No estoy completamente seguro, pero verifica el tipo de $item, ¿es un objeto o un array? El contenido dentro es un array, pero intenta convertirlo en un array para la función in_array, antes del bucle foreach (array)$item y en el selected() úsalo como un array así: $item['content_multiple']
