Crear más Cajas Meta según sea necesario
Me gustaría que los usuarios pudieran crear y eliminar campos de cajas meta adicionales según sea necesario.
Por ejemplo, digamos un podcast de música con una cantidad variable de canciones reproducidas por episodio. El usuario debería poder hacer clic en un botón que agregará campos adicionales para ingresar cada canción según sea necesario.
Idealmente, esto se haría sin el uso de un plugin, sino codificado en el archivo functions.
Aquí hay una solución completa que puedes implementar:
// Agregar meta box
function agregar_songs_meta_box() {
add_meta_box(
'songs_meta_box', // ID único
'Canciones del Episodio', // Título de la caja
'mostrar_songs_meta_box', // Función callback
'post' // Tipo de post (ajusta según necesites)
);
}
add_action('add_meta_boxes', 'agregar_songs_meta_box');
// Mostrar contenido del meta box
function mostrar_songs_meta_box($post) {
wp_nonce_field('songs_meta_box', 'songs_meta_box_nonce');
$songs = get_post_meta($post->ID, 'songs', true);
if (!is_array($songs)) $songs = array('');
?>
<div id="songs_container">
<?php foreach ($songs as $index => $song) : ?>
<p class="song-field">
<input type="text"
name="songs[]"
value="<?php echo esc_attr($song); ?>"
style="width: 80%">
<button type="button" class="remove-song button">Eliminar</button>
</p>
<?php endforeach; ?>
</div>
<button type="button" id="add_song" class="button">Agregar Canción</button>
<script>
jQuery(document).ready(function($) {
$('#add_song').on('click', function() {
var field = '<p class="song-field">' +
'<input type="text" name="songs[]" style="width: 80%">' +
'<button type="button" class="remove-song button">Eliminar</button>' +
'</p>';
$('#songs_container').append(field);
});
$('#songs_container').on('click', '.remove-song', function() {
$(this).parent('.song-field').remove();
});
});
</script>
<?php
}
// Guardar datos
function guardar_songs_meta_box($post_id) {
if (!isset($_POST['songs_meta_box_nonce'])) return;
if (!wp_verify_nonce($_POST['songs_meta_box_nonce'], 'songs_meta_box')) return;
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
if (!current_user_can('edit_post', $post_id)) return;
if (isset($_POST['songs'])) {
$songs = array_map('sanitize_text_field', $_POST['songs']);
$songs = array_filter($songs); // Eliminar campos vacíos
update_post_meta($post_id, 'songs', $songs);
}
}
add_action('save_post', 'guardar_songs_meta_box');
Este código:
- Crea una caja meta dinámica para canciones
- Permite agregar y eliminar campos de canción
- Guarda los datos de forma segura
- Incluye validación y sanitización
- Es totalmente responsive y amigable para el usuario
Para mostrar las canciones en el frontend, puedes usar:
$songs = get_post_meta(get_the_ID(), 'songs', true);
if (is_array($songs) && !empty($songs)) {
echo '<h3>Canciones en este episodio:</h3>';
echo '<ul>';
foreach ($songs as $song) {
echo '<li>' . esc_html($song) . '</li>';
}
echo '</ul>';
}

¿Quieres decir algo como esto?
y cuando haces clic en Agregar pistas se convierte en esto:
Si es esto lo que quieres decir, se hace creando un metabox que tiene una función simple de jQuery para agregar y eliminar campos, y los datos se guardan como un array en una sola fila de metadatos, aquí tienes:
add_action( 'add_meta_boxes', 'dynamic_add_custom_box' );
/* Hacer algo con los datos ingresados */
add_action( 'save_post', 'dynamic_save_postdata' );
/* Agrega un cuadro a la columna principal en las pantallas de edición de Entradas y Páginas */
function dynamic_add_custom_box() {
add_meta_box(
'dynamic_sectionid',
__( 'Mis Pistas', 'myplugin_textdomain' ),
'dynamic_inner_custom_box',
'post');
}
/* Imprime el contenido del cuadro */
function dynamic_inner_custom_box() {
global $post;
// Usar nonce para verificación
wp_nonce_field( plugin_basename( __FILE__ ), 'dynamicMeta_noncename' );
?>
<div id="meta_inner">
<?php
//obtener los metadatos guardados como un array
$songs = get_post_meta($post->ID,'songs',false);
$c = 0;
if ( count( $songs ) > 0 ) {
foreach( $songs as $track ) {
if ( isset( $track['title'] ) || isset( $track['track'] ) ) {
printf( '<p>Título de la canción <input type="text" name="songs[%1$s][title]" value="%2$s" /> -- Número de pista : <input type="text" name="songs[%1$s][track]" value="%3$s" /><span class="remove">%4$s</span></p>', $c, $track['title'], $track['track'], __( 'Eliminar Pista' ) );
$c = $c +1;
}
}
}
?>
<span id="here"></span>
<span class="add"><?php _e('Agregar Pistas'); ?></span>
<script>
var $ =jQuery.noConflict();
$(document).ready(function() {
var count = <?php echo $c; ?>;
$(".add").click(function() {
count = count + 1;
$('#here').append('<p> Título de la canción <input type="text" name="songs['+count+'][title]" value="" /> -- Número de pista : <input type="text" name="songs['+count+'][track]" value="" /><span class="remove">Eliminar Pista</span></p>' );
return false;
});
// El método live() fue desaprobado en jQuery versión 1.7, y eliminado en la versión 1.9. Usa el método on() en su lugar. Podemos usar .on
$(".remove").live('click', function() {
$(this).parent().remove();
});
});
</script>
</div><?php
}
/* Cuando se guarda la entrada, guarda nuestros datos personalizados */
function dynamic_save_postdata( $post_id ) {
// verificar si esto es una rutina de guardado automático.
// Si es así, nuestro formulario no se ha enviado, por lo que no queremos hacer nada
if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE )
return;
// verificar que esto proviene de nuestra pantalla y con la autorización adecuada,
// porque save_post puede activarse en otros momentos
if ( !isset( $_POST['dynamicMeta_noncename'] ) )
return;
if ( !wp_verify_nonce( $_POST['dynamicMeta_noncename'], plugin_basename( __FILE__ ) ) )
return;
// OK, estamos autenticados: necesitamos encontrar y guardar los datos
$songs = $_POST['songs'];
update_post_meta($post_id,'songs',$songs);
}

Parece que solo muestra "array(0) { } Añadir Pistas" cuando uso el código anterior.

Genial, eso solucionó ese problema, pero ahora los datos no parecen guardarse al actualizar. O al menos no muestra los datos como campos bajo Mi Pista, ni en Campos Personalizados. Si vuelvo a poner el var_dump, muestra "array(1) { [0]=> array(1) { [1]=> array(2) { ["title"]=> string(4) "test" ["track"]=> string(5) "teste" } } } Añadir Pistas"

Probé ese nuevo código y ahora muestra los datos guardados correctamente. Pero, ahora obtengo un error en cualquier publicación sin canciones guardadas que dice "Invalid argument supplied for foreach()" en esta línea "foreach($songs as $track ){". Además, cuando elimino canciones de una publicación, aparece un campo personalizado "Song" vacío en el cuadro de campos personalizados.

¿Alguna idea? Estoy dispuesto a pagarle a alguien para que esta funcionalidad funcione, parece que no falta mucho trabajo desde aquí para terminarlo.

No estoy realmente seguro de cuál es el problema, no es un código exacto, es un ejemplo y funciona bien en mi caso. Intenta cambiar if (count($songs) > 0){
por if(is_array($songs)){

¡Gracias! Funcionó perfectamente. He podido usar este código para crear campos más dinámicos, pero todavía hay un problema con que crean meta keys vacías. Si agrego una canción pero no agrego un enlace de show o una nota de show, termina creando meta keys vacías para ellos. O si vacío todos los elementos en un grupo, todavía queda una meta key vacía. Supongo que no es gran problema tener keys vacías en la base de datos, ¿verdad?

Aquí está el código, por si alguien tiene curiosidad sobre cómo lo implementé.
http://www.leschinskidesign.com.php5-10.websitetestlink.com/test/songs-function.txt http://www.leschinskidesign.com.php5-10.websitetestlink.com/test/showlinks-function.txt http://www.leschinskidesign.com.php5-10.websitetestlink.com/test/shownotes-function.txt

@Picard102 y @Bainternet - He probado su código, y funciona bien uno a la vez, pero no puedo hacer que los metaboxes guarden los datos si agrego un segundo metabox no relacionado. He cambiado los nonce-fields y todos los IDs y clases. ¿No sé qué estoy haciendo mal?

@Bainternet: http://wordpress.stackexchange.com/questions/23344/why-wont-my-metabox-data-save/

Hola, estoy planeando tener la misma función para mi tipo de publicación personalizada, esta vez un campo para fotos, pero las fotos deben ser subidas o obtenidas desde la biblioteca de medios. ¿Qué código debería editar? Gracias.

El método live() fue marcado como obsoleto en jQuery versión 1.7, y eliminado en la versión 1.9. Usa el método on() en su lugar.

Esto se hace a través de campos personalizados PERO nunca deberías usar nada que permita a los usuarios agregar crear o eliminar meta cajas. Estas escriben directamente en la base de datos, por lo que podrías crear muchos problemas para tu sitio si les das a los usuarios ese tipo de control. Es mucho más seguro crear el número máximo de campos personalizados que puedan necesitar y permitirles dejar algunos en blanco donde no sean necesarios.
Esto también es territorio de plugins. El archivo de funciones es específico del tema, mientras que los plugins son para funciones que se aplican al contenido del sitio, especialmente si deseas que ese contenido esté disponible independientemente del tema que uses.
Algunas sugerencias:

PERO nunca deberías usar nada que permita a los usuarios añadir o eliminar meta boxes
¿Por qué?

Mi única preocupación es que con cualquier plugin existe la posibilidad de que deje de tener soporte en el futuro. Siento que es más probable que pueda averiguar cómo arreglar una simple adición al archivo functions, que descifrar cómo arreglar un plugin.

Los plugins son esencialmente funciones que residen fuera del tema. Puedes tomar un plugin y copiar el código en functions.php y funcionará. De manera similar, puedes sacar funciones de functions.php, añadir el encabezado necesario para el plugin, y funcionará igual de bien en cuanto lo actives.
