menu custom fields

Cómo añadir campos personalizados a los menús de WordPress

Los menús de WordPress son una funcionalidad bastante completa, intuitiva y fácil de utilizar para crear menús. Pero a veces se nos pueden quedar cortos, sobre todo cuando el equipo de diseño da rienda suelta a su creatividad y utiliza el lienzo en blanco del que suelen partir para generar una estructura compleja.

A lo largo de los años, me ha tocado integrar multitud de menús, y llega un momento donde tienes que crear ajustes adicionales para que, por un lado el usuario pueda elegir la configuración que desee, y por otro pintar la salida HTML que corresponda.

Desde la versión 5.4 de WordPress tenemos a nuestra disposición el hook wp_nav_menu_item_custom_fields para crear nuestros propios campos personalizados para los menús.

Veamos con un ejemplo práctico cómo añadir un campo personalizado a los menús de WordPress. Añadamos un campo para permitir al usuario subir una imagen a los items de menú. Lo primero agrega al functions.php:

<?php

/**
 * Adds image custom field for WordPress Menus.
 *
 * @param int      $item_id Menu item ID.
 * @param WP_Post  $item    Menu item data object.
 * @param int      $depth   Depth of menu item. Used for padding.
 * @param stdClass $args    An object of menu item arguments.
 * @param int      $id      Nav menu ID.
 */
function add_nav_menu_custom_fields(int $item_id, WP_Post $item, int $depth, stdClass $args, int $id): void
{
    // Doesn't add custom fields in first menu level
    if (0 == $depth) {
        return;
    }

    wp_nonce_field('menu_custom_fields_action', 'menu_custom_fields_nonce');

    $menu_item_image = get_post_meta($item_id, 'menu_image_item', true);
    $display = empty($menu_item_image) ? 'display:none' : '';
    $menu_item_image_src = wp_get_attachment_url($menu_item_image); ?>

    <p class="description description-wide">
        <label for="menu_image_item-<?php echo $item_id; ?>">Imagen / Icono</label>
        <div class="menu-item-image">
            <div class="menu-item-image-buttons">
                <a href="#" class="button nav-menu-item-upload">Subir imagen</a>
                <a href="#" class="button nav-menu-item-delete" style="<?php echo $display; ?>">Eliminar</a>
                <input type="hidden" id="menu_image_item-<?php echo $item_id; ?>" name="menu_image_item[<?php echo $item_id; ?>]" value="<?php echo $menu_item_image; ?>" />
            </div>

            <div class="menu-item-image-src">
                <img src="<?php echo $menu_item_image_src; ?>" style="<?php echo $display; ?>" />
            </div>
        </div>
    </p>
<?php
}
add_action('wp_nav_menu_item_custom_fields', 'add_nav_menu_custom_fields', 10, 5);

Este hook acepta 5 parámetros, y con ellos podremos (como en el ejemplo) evitar añadir el custom field en el primer nivel de menú. El HTML de este hook se añadirá al item de menú en el dashboard de WordPress:

menu custom fields

Para que el botón de «Subir imagen» funcione como la galería de medios, será también necesario incluir unas librerías de JavaScript y un JS personalizado:

<?php

function nav_menu_custom_fields_assets(): void
{
    wp_enqueue_media();
    wp_enqueue_script('nav-menu-custom-fields-js', get_template_directory_uri() . '/assets/js/nav-menu.js', ['jquery'], '1.0');
}
add_action('admin_enqueue_scripts', 'nav_menu_custom_fields_assets');

Nuestro archivo JavaScript personalizado (nav-menus.js) tendrá el siguiente contenido:

jQuery(document).ready(function ($) {
  $('.nav-menu-item-upload').on("click", function() {
    var send_attachment_bkp = wp.media.editor.send.attachment;
    var button = $(this);

    wp.media.editor.send.attachment = function(props, attachment) {
      $(button).next().show();
      $(button).next().next().val(attachment.id).show();
      $(button).closest('.menu-item-image-buttons').next().find('img').attr('src', attachment.url).show();

      wp.media.editor.send.attachment = send_attachment_bkp;
    }
    wp.media.editor.open(button);

    return false;       
  });

  $('.nav-menu-item-delete').on("click", function() {
    var button = $(this);

    $(button).hide();
    $(button).next().val('');
    $(button).closest('.menu-item-image-buttons').next().find('img').attr('src', '').hide();

    return false;       
  });
});

Esto básicamente lo que hace es dotar de la funcionalidad de la librería de medios al botón «Subir imagen». También añadiremos funcionalidad al botón «Eliminar», vaciando el input correspondiente para que cuando guardemos el menú, se borre la configuración que estuviera establecida.

Para manejar el guardado de datos, tendremos que añadir también la siguiente función:

<?php

/**
 * Saves custom fields data.
 *
 * @param int   $menu_id         ID of the updated menu.
 * @param int   $menu_item_db_id ID of the updated menu item.
 * @param array $args            An array of arguments used to update a menu item.
 */
function update_nav_menu_custom_fields(int $menu_id, int $menu_item_db_id, array $args)
{
    if (! wp_verify_nonce($_POST['menu_custom_fields_nonce'] ?? '', 'menu_custom_fields_action')) {
        return $menu_id;
    }

    $menu_item_image = sanitize_text_field($_POST['menu_image_item'][$menu_item_db_id] ?? '');

    update_post_meta($menu_item_db_id, 'menu_image_item', $menu_item_image);
}
add_action('wp_update_nav_menu_item', 'update_nav_menu_custom_fields', 10, 3);

Y con esto ya estaría todo. Ahora el siguiente paso será crear un Walker para generar el menú, y donde podrás consultar si el item correspondiente tiene imagen o no para pintarlo en el HTML resultante.

¿Te ha resultado útil esta información? 🍺

Si este post te ha resuelto un problema, invítame a un café o a una cerveza. Con este pequeño gesto me animas a seguir escribiendo.

Comentarios

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *:

  • El fin del tratamiento es únicamente la moderación de comentarios para evitar spam
  • La legitimación es tu consentimiento al comentar
  • No se comunicará ningún dato a terceros salvo por obligación legal
  • Tienes derecho al acceso, rectificación y eliminación de los comentarios