向 WordPress 菜单项添加字段 – 自定义插件

已发表: 2021-05-19

在上一篇文章中,我们研究了如何使用 WordPress 5.4 中引入的wp_nav_menu_item_custom_fields操作挂钩将自己的自定义字段添加到菜单项。 我们详细介绍了实现这一目标的两条途径; 通过将一些自定义代码添加到functions.php或使用 WordPress 插件。

在本文中,我们将重新创建相同的字段,但这次我们将通过从头开始创建自己的插件来实现。

让我们开始吧。

插件创建

我们不会深入了解创建 WordPress 插件的基本细节,因为这已在我们的深入文章“如何构建您的第一个 WordPress 插件”中进行了介绍。 分步指南”。 我们将跳过在wp-content/plugin/文件夹下创建我们的插件文件夹。 接下来,我们将自定义插件文件夹命名为“menu-item-field-creator”,并在其中创建一个名为menu-item-field-creator.php的文件。

在此之后,我们将使用我们最喜欢的文本编辑器打开这个文件并添加下面的代码。 此代码具有将插件引入 WordPress 核心的效果。

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

您可能会注意到,我们定义了插件名称和描述,因为我们希望它们显示在管理插件区域中。 为了这个例子,我们不会定义任何其他的头字段。

完成这些步骤后,让我们前往管理区域中的插件部分并检查插件是否正确显示。

接下来,我们将插入我们的第一个函数代码,它将建立我们的主类和一些函数。

简单的方法

让这个插件工作的最简单方法是将我们在上一篇文章中编写的代码插入到插件的主 PHP 文件中,以便最终内容如下所示:

 <?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 );

此时,如果您激活插件,您应该会发现它工作得很好。 然而,有一种方法可以使用不同的编码风格来实现相同的结果。

使用面向对象编程

如我们在相关文章中描述的那样,要以更加面向对象的方法实现相同的结果,请按照以下说明进行操作。

首先,清空插件主 PHP 文件的内容,除了标题注释并插入以下行:

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

到目前为止,我们对这段代码所做的是定义将包含整个功能的MyCP_Menu_Item_Field_Creator包装类。 最后,我们实例化一个对象。

请务必记住,您定义的主类名称将是全局可用的,因此您必须确保它是唯一的,并且任何其他插件或主题都不可能使用相同的名称。 这就是为什么建议您使用自定义前缀,例如我们上面使用的MyCP_

在类中,我们现在将添加一些功能。 我们的插件主 PHP 文件的最终内容将如下所示:

 <?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();

我们从方法名称中删除了前缀,因为我们现在有一个类前缀。

__construct函数中,我们定义了我们使用的钩子以及它们将执行的回调函数。 接下来我们介绍了menu_item_sub回调函数,它将显示管理员用户可以填写项目副标题的输入字段。

在此之后,我们使用save_menu_item_sub方法保存输入,最后,使用show_menu_item_sub回调,我们在前端菜单中显示值(如果可用)。

扩展步行者

在上面的示例中,我们在菜单项标题中包含了自定义菜单字段,而没有更改菜单树数据的 HTML 输出。 但是,如果我们想将副标题字段添加为单独的 HTML 元素,例如标题元素链接之外的<div>元素,该怎么办?

这是我们必须再次与 Walker 类一起工作的地方。 正如我们在文章“熟悉 WordPress Walker 类”中所见,通过扩展 Walker,您可以自定义树状数据的结构。 在这种情况下,这将是菜单。

这当然意味着我们只需要更改与自定义字段的前端显示相关的代码。 所以让我们用这个替换整个代码:

 <?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>'; } } } }

您可能已经注意到我们删除了show_menu_item_sub方法并以不同的方式处理前端菜单项结构。 我们在主类之外引入了自定义 Walker 类My_Custom_Nav_Walker ,并通过menu_item_sub_custom_walker方法将“walker”参数的默认值更改为My_Custom_Nav_Walker 。 这样,我们在自定义 Walker 中提供的菜单 HTML 输出将应用于我们的前端。

让我们检查一下结果。

正如我们所看到的,我们这次的描述按照我们的意图放在了菜单项链接的 href 之外。

更进一步

在结束本文之前,值得一提的是,我们特意使用了“字幕”示例,因为它易于操作和理解。

如果您想推动自己,那么我们建议您创建自己的场景进行试验。 例如,尝试创建一个不同的输出,允许管理员用户定义允许哪个用户角色查看菜单项。

作为开始提示,我们将为您提供输出。 替换当前的输出方式(如下图)

 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 }

有了这个:

 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 }

现在尝试创建一种方法来保存更改以及使用正确挂钩显示当前/保存值的方法。

结论

在您了解可用于执行此操作的工具之前,自定义 WordPress 菜单可能会令人沮丧。 希望本文能让您深入了解可能实现的目标以及实现此类任务的方法。

也可以看看

  • 向 WordPress 菜单项添加自定义字段