Дополнительные поля для меню WP_Nav_Menu

C версии WordPress 5.4 появились новые хуки, которые позволяют более гибко настроить меню WordPress (WP Nav Menu). В частности стало возможным легко добавлять пользовательские (дополнительные) поля в меню WordPress.

Речь идет о хуках wp_nav_menu_item_custom_fields и wp_nav_menu_item_custom_fields_customize_template. С их помощью теперь можно легко добавлять собственные произвольные поля к пунктам меню как на странице редактирования меню в админке, так и в панели Customizer.

В этой заметке мы рассмотрим использование хука wp_nav_menu_item_custom_fields для добавления собственных пользовательских полей в пункты меню. Также рассмотрим плагины, которые позволяют сделать тоже самое без написания кода.

Добавим поля в меню с помощью PHP кода

Код ниже использует два хука для создания нового поля в меню _menu_item_svg_icon:

  • wp_nav_menu_item_custom_fields - позволяет вывести HTML код в админ-панели меню.
  • wp_update_nav_menu_item - позволяет сохранить добавленные поля в метаполя элемента меню (напомню что элементы меню хранятся в таблице wp_posts и используют стандартные метаполя постов).

Также используется два хука, чтобы обработать вывод созданного поля. Здесь в поле храниться название файла иконки SVG. При выводе меню и немного костыльным, но простым способом добавляем плейсхолдер в базовую HTML разметку элемента и затем заменяем его на SVG картинку.

<?php

final class WP_Nav_Menu_Custom_Fields {

	public static array $field_keys = [

		'_menu_item_svg_icon' => [
			'title' => 'SVG Icon Name',
			'desc' => 'Set svg icon name (not url)',
		],

		'test_number_field' => [
			'title' => 'Number One',
			'type' => 'number',
			'size' => 'thin', // thin | wide
			// 'desc' => 'some text',
		],

		'test_number_field_two' => [
			'title' => 'Number Two',
			'type' => 'number',
			'size' => 'thin', // thin | wide
			// 'desc' => 'some text',
		],

	];

	public static function init(): void {

		if( is_admin() ){
			add_action( 'wp_nav_menu_item_custom_fields', [ __CLASS__, 'add_fileds' ], 10, 2 );
			add_action( 'wp_update_nav_menu_item', [ __CLASS__, 'save_fields' ], 10, 2 );
		}
		// front
		else {
			add_filter( 'walker_nav_menu_start_el', [ __CLASS__, 'nav_menu_start_el' ], 10, 2 );
			add_filter( 'wp_nav_menu_args', [ __CLASS__, 'nav_menu_args' ] );
		}

	}

	public static function add_fileds( $item_id, $item ) {

		foreach( self::$field_keys as $meta_key => $data ){

			$value = get_post_meta( $item_id, $meta_key, true );
			$title = $data['title'];
			$type = $data['type'] ?? 'text';
			$size = $data['size'] ?? 'wide';

			$desc = empty( $data['desc'] ) ? '' : '<span class="description">'. $data['desc'] .'</span>';
			?>
			<p class="field-<?= $meta_key ?> description description-<?= $size ?>">
				<?= $title ?>
				<br/>
				<input class="widefat edit-menu-item-<?= $meta_key ?>"
					   type="<?= $type ?>"
					   name="<?= sprintf( '%s[%s]', $meta_key, $item_id ) ?>"
					   id="menu-item-<?= $item_id ?>"
					   value="<?= esc_attr( $value ) ?>"/>

				<?= $desc ?>
			</p>
			<?php
		}
	}

	public static function save_fields( $menu_id, $item_id ) {

		foreach( self::$field_keys as $meta_key => $data ){
			self::save_field( $menu_id, $item_id, $meta_key );
		}
	}

	private static function save_field( $menu_id, $item_id, $meta_key ) {

		if( ! isset( $_POST[ $meta_key ][ $item_id ] ) ){
			return;
		}

		$val = $_POST[ $meta_key ][ $item_id ];

		if( $val ){
			update_post_meta( $item_id, $meta_key, sanitize_text_field( $val ) );
		}
		else{
			delete_post_meta( $item_id, $meta_key );
		}

	}

	public static function nav_menu_start_el( $item_output, $post  ){

		$svg = $post->_menu_item_svg_icon ?: '';
		if( $svg ){
			$svg = get_svg( $svg );
		}

		return str_replace( '{SVG}', $svg, $item_output );
	}

	public static function nav_menu_args( $args ){

		if( empty( $args['link_before'] ) ){
			$args['link_before'] = '{SVG}';
		}

		return $args;
	}

}

После установки этого кода (создайте файл из этого кода и подключите его в functions.php), его нужно запустить такой строкой в файле functions.php:

WP_Nav_Menu_Custom_Fields::init();

Теперь, когда мы зайдем на страницу админ-панели "Тема > Меню (Appearance > Menus)" мы увидим там новые поля:

Получение полей

Чтобы получить поля на выводе используйте функцию get_post_meta():

$field_value = get_post_meta( $item_id, $field_key, true );

Такой код можно использовать например в методе start_el() при создании своего класса для обработки вывода меню. Или в хуках, которые меняю вывод навигационного меню.

Добавим поля в меню с помощью плагина Advanced Custom Fields (ACF)

Очень популярный и универсальный плагин Advanced Custom Fields снова демонстрирует свою гибкость, позволяя добавлять пользовательские поля в меню WordPress.

После установки и активации плагина, откройте его и нажмите на кнопку "Добавить новое", чтобы добавить поля. Выберите 'Menu Item' в правилах расположения. Следуйте инструкциям и обновите поля по мере необходимости.

После публикации поля вы можете перейти в меню WordPress из области администратора, чтобы увидеть новое поле, которое вы создали. Довольно просто!

Добавим поля в меню с помощью плагина WP Menu Custom Fields

Плагин WP Menu Custom Fields - это относительно новый плагин, который, как следует из названия, поможет вам добавить пользовательские поля в пункты вашего меню. Вы можете добавить пользовательский текст, изображение, шорткод или пользовательский HTML.

Вместо того чтобы создавать пользовательские пункты меню через специальный интерфейс плагина (как это делается с Advanced Custom Fields), WP Menu Custom Fields добавляет редактируемые параметры непосредственно в любой пункт меню в области редактирования меню администратора.

Он довольно прост в использовании и представляет собой удобный способ добавления пользовательских полей и другого содержимого в пункты меню. Документация плагина также предоставляет крючки плагина, которые вы можете использовать для дальнейшей настройки HTML-генерации каждой функции.