Agregar campos a los elementos del menú de WordPress: complemento personalizado

Publicado: 2021-05-19

En un artículo anterior, analizamos cómo puede agregar su propio campo personalizado a los elementos del menú utilizando el gancho de acción wp_nav_menu_item_custom_fields que se introdujo en WordPress 5.4. Detallamos dos rutas para lograr esto; agregando un código personalizado a functions.php o usando un complemento de WordPress.

En este artículo, recrearemos el mismo campo, pero esta vez lo haremos creando nuestro propio complemento desde cero.

Empecemos.

Creación de complementos

No entraremos en los detalles básicos de la creación de un complemento de WordPress, ya que esto se cubrió en nuestro artículo detallado 'Cómo crear su primer complemento de WordPress'. Una guía paso a paso'. Pasaremos directamente a crear nuestra carpeta de complementos en la carpeta wp-content/plugin/ . A continuación, nombraremos nuestra carpeta de complementos personalizados "menu-item-field-creator" y dentro de ella crearemos un archivo llamado menu-item-field-creator.php .

Después de esto, abriremos este archivo con nuestro editor de texto favorito y agregaremos el código a continuación. Este código tiene el efecto de introducir el complemento en el núcleo de WordPress.

 <?php /* Plugin Name: Menu Item Field Creator Description: My custom plugin to create menu item fields */

Como puede notar, definimos el nombre y la descripción del complemento como queremos que se muestren en el área del complemento de administración. Por el bien de este ejemplo, no definiremos ningún otro campo de encabezado.

Una vez realizados estos pasos, vayamos a nuestra sección Complementos en el área de administración y verifiquemos si el complemento se muestra correctamente.

A continuación, insertaremos nuestro primer código funcional que establecerá nuestra clase principal y algunas funciones.

La forma sencilla

La forma más sencilla de hacer que este complemento funcione es insertar el código que escribimos en nuestro artículo anterior en el archivo PHP principal del complemento para que el contenido final se vea así:

 <?php /* Plugin Name: Menu Item Field Creator Description: My custom plugin to create menu item fields */ /** * Add the field. */ function pr_menu_item_sub( $item_id, $item ) { $menu_item_sub = get_post_meta( $item_id, '_menu_item_sub', true ); ?> <div> <span class="subtitle"><?php _e( 'Subtitle', 'menu-item-sub' ); ?></span><br /> <input type="hidden" class="nav-menu-id" value="<?php echo $item_id; ?>" /> <div class="logged-input-holder"> <input type="text" name="menu_item_sub[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $menu_item_sub ); ?>" /> </div> </div> <?php } add_action( 'wp_nav_menu_item_custom_fields', 'pr_menu_item_sub', 10, 2 ); /** * Save input. */ function save_menu_item_sub( $menu_id, $menu_item_db_id ) { if ( isset( $_POST['menu_item_sub'][ $menu_item_db_id ] ) ) { $sanitized_data = sanitize_text_field( $_POST['menu_item_sub'][ $menu_item_db_id ] ); update_post_meta( $menu_item_db_id, '_menu_item_sub', $sanitized_data ); } else { delete_post_meta( $menu_item_db_id, '_menu_item_sub' ); } } add_action( 'wp_update_nav_menu_item', 'save_menu_item_sub', 10, 2 ); /** * Show the Menu Field Value. */ function show_menu_item_sub( $title, $item ) { if ( is_object( $item ) && isset( $item->ID ) ) { $menu_item_sub = get_post_meta( $item->ID, '_menu_item_sub', true ); if ( ! empty( $menu_item_sub ) ) { $title .= '<p class="menu-item-sub">' . $menu_item_sub . '</p>'; } } return $title; } add_filter( 'nav_menu_item_title', 'show_menu_item_sub', 10, 2 );

En este punto, si activa el complemento, debería encontrar que funciona bien. Sin embargo, existe una manera de lograr los mismos resultados utilizando un estilo de codificación diferente.

Uso de la programación orientada a objetos

Para lograr los mismos resultados en un enfoque más orientado a objetos, como lo describimos en nuestro artículo relacionado, siga las instrucciones a continuación.

Para comenzar, vacíe el contenido del archivo PHP principal de su complemento, excepto el comentario del encabezado e inserte estas líneas:

 class MyCP_Menu_Item_Field_Creator { } $mycp_menu_item_field_creator = new MyCP_Menu_Item_Field_Creator();

Lo que hemos hecho con este código hasta ahora es definir la clase contenedora MyCP_Menu_Item_Field_Creator que contendrá toda la funcionalidad. Por último, instanciamos un objeto.

Es muy importante recordar que el nombre de la clase principal que defina estará disponible globalmente y, por lo tanto, debe asegurarse de que sea único y que no haya posibilidad de que ningún otro complemento o tema use el mismo nombre. Es por eso que se recomienda que use un prefijo personalizado como el MyCP_ que usamos anteriormente.

Dentro de la clase ahora agregaremos algunas funciones. El contenido final del archivo PHP principal de nuestro complemento se verá así:

 <?php /* Plugin Name: Menu Item Field Creator Description: My custom plugin to create menu item fields */ class MyCP_Menu_Item_Field_Creator { public function __construct() { add_action( 'wp_nav_menu_item_custom_fields', array( $this, 'menu_item_sub' ), 10, 2 ); add_action( 'wp_update_nav_menu_item', array( $this, 'save_menu_item_sub' ), 10, 2 ); add_action( 'nav_menu_item_title', array( $this, 'show_menu_item_sub' ), 10, 2 ); } public function menu_item_sub( $item_id, $item ) { $menu_item_sub = get_post_meta( $item_id, '_menu_item_sub', true ); ?> <div> <span class="subtitle"><?php _e( 'Subtitle', 'menu-item-sub' ); ?></span><br /> <input type="hidden" class="nav-menu-id" value="<?php echo $item_id; ?>" /> <div class="logged-input-holder"> <input type="text" name="menu_item_sub[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $menu_item_sub ); ?>" /> </div> </div> <?php } public function save_menu_item_sub( $menu_id, $menu_item_db_id ) { if ( isset( $_POST['menu_item_sub'][ $menu_item_db_id ] ) ) { $sanitized_data = sanitize_text_field( $_POST['menu_item_sub'][ $menu_item_db_id ] ); update_post_meta( $menu_item_db_id, '_menu_item_sub', $sanitized_data ); } else { delete_post_meta( $menu_item_db_id, '_menu_item_sub' ); } } public function show_menu_item_sub( $title, $item ) { if ( is_object( $item ) && isset( $item->ID ) ) { $menu_item_sub = get_post_meta( $item->ID, '_menu_item_sub', true ); if ( ! empty( $menu_item_sub ) ) { $title .= '<p class="menu-item-sub">' . $menu_item_sub . '</p>'; } } return $title; } } $mycp_menu_item_field_creator = new MyCP_Menu_Item_Field_Creator();

Eliminamos los prefijos de los nombres de los métodos, ya que ahora tenemos un prefijo de clase.

En la función __construct definimos los ganchos que usamos junto con sus funciones de devolución de llamada que se ejecutarán. A continuación, presentamos la función de devolución de llamada menu_item_sub que mostrará el campo de entrada donde el usuario administrador puede completar el subtítulo del elemento.

Después de esto, guardamos la entrada con el método save_menu_item_sub y finalmente, con la devolución de llamada show_menu_item_sub , mostramos los valores, si están disponibles, en el menú frontal.

Expandiendo el andador

En el ejemplo anterior, incluimos el campo de menú personalizado dentro del título del elemento del menú, sin alterar la salida HTML de los datos del árbol del menú. Pero, ¿qué pasaría si quisiéramos agregar el campo de subtítulos como un elemento HTML separado, como un elemento <div> fuera del enlace del elemento de título?

Aquí es donde tenemos que trabajar una vez más con la clase Walker. Como vimos en nuestro artículo 'Familiarizándose con la clase Walker de WordPress', al extender Walker puede personalizar la estructura de los datos en forma de árbol. En este caso este será el menú.

Esto significa, por supuesto, que solo tenemos que modificar nuestro código relacionado con la visualización frontal de nuestro campo personalizado. Así que vamos a reemplazar todo el código con este:

 <?php /* Plugin Name: Menu Item Field Creator Description: My custom plugin to create menu item fields */ class MyCP_Menu_Item_Field_Creator { public function __construct() { add_action( 'wp_nav_menu_item_custom_fields', array( $this, 'menu_item_sub' ), 10, 2 ); add_action( 'wp_update_nav_menu_item', array( $this, 'save_menu_item_sub' ), 10, 2 ); add_filter( 'wp_nav_menu_args', array( $this, 'menu_item_sub_custom_walker' ) ); } public function menu_item_sub( $item_id, $item ) { $menu_item_sub = get_post_meta( $item_id, '_menu_item_sub', true ); ?> <div> <span class="subtitle"><?php _e( 'Subtitle', 'menu-item-sub' ); ?></span><br /> <input type="hidden" class="nav-menu-id" value="<?php echo $item_id; ?>" /> <div class="logged-input-holder"> <input type="text" name="menu_item_sub[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $menu_item_sub ); ?>" /> </div> </div> <?php } public function save_menu_item_sub( $menu_id, $menu_item_db_id ) { if ( isset( $_POST['menu_item_sub'][ $menu_item_db_id ] ) ) { $sanitized_data = sanitize_text_field( $_POST['menu_item_sub'][ $menu_item_db_id ] ); update_post_meta( $menu_item_db_id, '_menu_item_sub', $sanitized_data ); } else { delete_post_meta( $menu_item_db_id, '_menu_item_sub' ); } } public function menu_item_sub_custom_walker( $args ) { if ( class_exists( 'My_Custom_Nav_Walker' ) ) { $args['walker'] = new My_Custom_Nav_Walker(); } else { echo 'DOES NOT EXIST'; } return $args; } } $mycp_menu_item_field_creator = new MyCP_Menu_Item_Field_Creator(); if ( ! class_exists( 'My_Custom_Nav_Walker' ) ) { class My_Custom_Nav_Walker extends Walker_Nav_Menu { public function start_el( &$output, $item, $depth=0, $args=[], $id=0 ) { $menu_item_sub = get_post_meta( $item->ID, '_menu_item_sub', true ); $output .= '<li class="' . implode( ' ', $item->classes ) . '">'; if ( $item->url && $item->url != '#' ) { $output .= '<a href="' . $item->url . '">'; } else { $output .= '<span>'; } $output .= $item->title; if ( $item->url && $item->url != '#' ) { $output .= '</a>'; } else { $output .= '</span>'; } if ( ! empty( $menu_item_sub ) ) { $output .= '<div class="menu-item-sub">' . $menu_item_sub . '</div>'; } } } }

Es posible que haya notado que eliminamos el método show_menu_item_sub y trabajamos con la estructura del elemento del menú frontal de manera diferente. Introdujimos nuestra clase Walker personalizada My_Custom_Nav_Walker fuera de nuestra clase principal y a través de nuestro método menu_item_sub_custom_walker cambiamos el valor predeterminado para el argumento 'walker' en My_Custom_Nav_Walker . De esta manera, la salida HTML del menú que proporcionamos en nuestro Walker personalizado se aplicará en nuestra interfaz.

Comprobemos los resultados.

Como podemos ver, nuestra descripción esta vez se coloca fuera del href del enlace del elemento del menú como pretendíamos.

Llevándolo más lejos

Antes de concluir esto, vale la pena mencionar que usamos deliberadamente el ejemplo de "Subtítulo" porque es simple de hacer y entender.

Si quiere esforzarse, le sugerimos que cree sus propios escenarios para experimentar. Intente, por ejemplo, crear una salida diferente que permita al usuario administrador definir qué rol de usuario puede ver el elemento del menú.

Como sugerencia inicial, le proporcionaremos la salida. Reemplace el método de salida actual (que se muestra a continuación)

 function menu_item_sub( $item_id, $item ) { $menu_item_sub = get_post_meta( $item_id, '_menu_item_sub', true ); ?> <div> <span class="subtitle"><?php _e( 'Subtitle', 'menu-item-sub' ); ?></span><br /> <input type="hidden" class="nav-menu-id" value="<?php echo $item_id; ?>" /> <div class="logged-input-holder"> <input type="text" name="menu_item_sub[<?php echo $item_id; ?>]" value="<?php echo esc_attr( $menu_item_sub ); ?>" /> </div> </div> <?php }

Con este:

 function PREFIX_Menu_Item_Roles() { global $wp_roles; $display_roles = apply_filters( 'nav_menu_roles', $wp_roles->role_names ); if ( ! $display_roles ) return; ?> <p class="field-nav_menu_logged_in_out nav_menu_logged_in_out nav_menu_logged_in_out-thin"> <fieldset> <legend><?php _e( 'Display Mode', 'nav-menu-roles' ); ?></legend> <label for="edit-menu-item-role_logged_in"> <input type="radio" class="edit-menu-item-logged_in_out" value="in" name="menu-item-role_logged_in" /> <?php _e( 'Logged In Users', 'nav-menu-roles' ); ?><br/> </label> <label for="edit-menu-item-role_logged_out"> <input type="radio" class="edit-menu-item-logged_in_out" value="out" name="menu-item-role_logged_out" /> <?php _e( 'Logged Out Users', 'nav-menu-roles' ); ?><br/> </label> <label for="edit-menu-item-role_everyone"> <input type="radio" class="edit-menu-item-logged_in_out" value="" name="menu-item-role_everyone" /> <?php _e( 'Everyone', 'nav-menu-roles' ); ?><br/> </label> </fieldset> </p> <?php }

Ahora intente crear un método que guarde los cambios junto con uno que muestre el valor actual/guardado usando los ganchos correctos.

Conclusión

Personalizar un menú de WordPress puede ser frustrante hasta que conozca las herramientas disponibles para hacerlo. Esperamos que este artículo le haya dado algunas ideas sobre lo que es posible lograr y las formas en que puede implementar tareas como esta.

Ver también

  • Agregar campos personalizados a los elementos del menú de WordPress