Crear más Cajas Meta según sea necesario

13 jun 2011, 02:22:28
Vistas: 24.6K
Votos: 49

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>';
}
2
Comentarios

¡Estás describiendo el metabox de campos personalizados integrado!

Bainternet Bainternet
13 jun 2011 02:42:36

Supongo que esencialmente así es como funciona, pero solo está configurado para hacer un campo a la vez, y no es muy amigable para el usuario final.

Picard102 Picard102
13 jun 2011 03:11:58
Todas las respuestas a la pregunta 2
15
56

¿Quieres decir algo como esto?

Descripción de la imagen aquí

y cuando haces clic en Agregar pistas se convierte en esto:

Descripción de la imagen aquí

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);
    }
13 jun 2011 10:41:06
Comentarios

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

Picard102 Picard102
14 jun 2011 05:06:47

Sí, simplemente elimina var_dump($songs);

Bainternet Bainternet
14 jun 2011 10:44:17

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"

Picard102 Picard102
15 jun 2011 09:16:09

@Picard102 : arreglé el código y actualicé mi respuesta, avísame.

Bainternet Bainternet
15 jun 2011 23:14:09

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.

Picard102 Picard102
17 jun 2011 03:25:27

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

Picard102 Picard102
26 jun 2011 22:02:00

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)){

Bainternet Bainternet
27 jun 2011 11:01:55

¡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?

Picard102 Picard102
2 jul 2011 20:30:23

@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?

gillespieza gillespieza
18 jul 2011 17:50:11

@Amanda Tendría que ver tu código para saber cuál es el problema.

Bainternet Bainternet
18 jul 2011 23:58:23

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.

markyeoj markyeoj
31 ago 2013 15:08:17

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.

inrsaurabh inrsaurabh
21 ago 2018 09:53:49

si estás usando una versión actualizada de jQuery

inrsaurabh inrsaurabh
21 ago 2018 09:59:22
Mostrar los 10 comentarios restantes
4

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:

http://wordpress.org/extend/plugins/verve-meta-boxes/

http://wordpress.org/extend/plugins/more-fields/

13 jun 2011 03:20:18
Comentarios

PERO nunca deberías usar nada que permita a los usuarios añadir o eliminar meta boxes

¿Por qué?

Travis Northcutt Travis Northcutt
13 jun 2011 15:34:01

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.

Picard102 Picard102
14 jun 2011 05:08:23

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.

Elpie Elpie
14 jun 2011 05:14:09

Bueno saberlo. Probé los dos plugins que sugeriste, pero ninguno de ellos realmente me permitió hacer lo que necesitaba y que no pudiera hacer ya sin el plugin. De todos modos, gracias por las sugerencias.

Picard102 Picard102
14 jun 2011 05:26:05