wp_update_nav_menu_item( int $menu_id, int $menu_item_db_id, array $menu_item_data = array(), bool $fire_after_hooks = true ): int|WP_Error

Saves the properties of a menu item or create a new one.

Description

The menu-item-title, menu-item-description and menu-item-attr-title are expected to be pre-slashed since they are passed directly to APIs that expect slashed data.

Parameters

$menu_idintrequired
The ID of the menu. If 0, makes the menu item a draft orphan.
$menu_item_db_idintrequired
The ID of the menu item. If 0, creates a new menu item.
$menu_item_dataarrayoptional
The menu item’s data.

Default:array()

$fire_after_hooksbooloptional
Whether to fire the after insert hooks.

Default:true

Return

int|WP_Error The menu item’s database ID or WP_Error object on failure.

Source

function wp_update_nav_menu_item( $menu_id = 0, $menu_item_db_id = 0, $menu_item_data = array(), $fire_after_hooks = true ) {	$menu_id = (int) $menu_id;	$menu_item_db_id = (int) $menu_item_db_id;	// Make sure that we don't convert non-nav_menu_item objects into nav_menu_item objects.	if ( ! empty( $menu_item_db_id ) && ! is_nav_menu_item( $menu_item_db_id ) ) {	return new WP_Error( 'update_nav_menu_item_failed', __( 'The given object ID is not that of a menu item.' ) );	}	$menu = wp_get_nav_menu_object( $menu_id );	if ( ! $menu && 0 !== $menu_id ) {	return new WP_Error( 'invalid_menu_id', __( 'Invalid menu ID.' ) );	}	if ( is_wp_error( $menu ) ) {	return $menu;	}	$defaults = array(	'menu-item-db-id' => $menu_item_db_id,	'menu-item-object-id' => 0,	'menu-item-object' => '',	'menu-item-parent-id' => 0,	'menu-item-position' => 0,	'menu-item-type' => 'custom',	'menu-item-title' => '',	'menu-item-url' => '',	'menu-item-description' => '',	'menu-item-attr-title' => '',	'menu-item-target' => '',	'menu-item-classes' => '',	'menu-item-xfn' => '',	'menu-item-status' => '',	'menu-item-post-date' => '',	'menu-item-post-date-gmt' => '',	);	$args = wp_parse_args( $menu_item_data, $defaults );	if ( 0 === $menu_id ) {	$args['menu-item-position'] = 1;	} elseif ( 0 === (int) $args['menu-item-position'] ) {	$menu_items = array();	if ( 0 !== $menu_id ) {	$menu_items = (array) wp_get_nav_menu_items( $menu_id, array( 'post_status' => 'publish,draft' ) );	}	$last_item = array_pop( $menu_items );	if ( $last_item && isset( $last_item->menu_order ) ) {	$args['menu-item-position'] = 1 + $last_item->menu_order;	} else {	$args['menu-item-position'] = count( $menu_items );	}	}	$original_parent = 0 < $menu_item_db_id ? get_post_field( 'post_parent', $menu_item_db_id ) : 0;	if ( 'custom' === $args['menu-item-type'] ) {	// If custom menu item, trim the URL.	$args['menu-item-url'] = trim( $args['menu-item-url'] );	} else {	/* * If non-custom menu item, then: * - use the original object's URL. * - blank default title to sync with the original object's title. */	$args['menu-item-url'] = '';	$original_title = '';	if ( 'taxonomy' === $args['menu-item-type'] ) {	$original_object = get_term( $args['menu-item-object-id'], $args['menu-item-object'] );	if ( $original_object instanceof WP_Term ) {	$original_parent = get_term_field( 'parent', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' );	$original_title = get_term_field( 'name', $args['menu-item-object-id'], $args['menu-item-object'], 'raw' );	}	} elseif ( 'post_type' === $args['menu-item-type'] ) {	$original_object = get_post( $args['menu-item-object-id'] );	if ( $original_object instanceof WP_Post ) {	$original_parent = (int) $original_object->post_parent;	$original_title = $original_object->post_title;	}	} elseif ( 'post_type_archive' === $args['menu-item-type'] ) {	$original_object = get_post_type_object( $args['menu-item-object'] );	if ( $original_object instanceof WP_Post_Type ) {	$original_title = $original_object->labels->archives;	}	}	if ( wp_unslash( $args['menu-item-title'] ) === wp_specialchars_decode( $original_title ) ) {	$args['menu-item-title'] = '';	}	// Hack to get wp to create a post object when too many properties are empty.	if ( '' === $args['menu-item-title'] && '' === $args['menu-item-description'] ) {	$args['menu-item-description'] = ' ';	}	}	// Populate the menu item object.	$post = array(	'menu_order' => $args['menu-item-position'],	'ping_status' => 0,	'post_content' => $args['menu-item-description'],	'post_excerpt' => $args['menu-item-attr-title'],	'post_parent' => $original_parent,	'post_title' => $args['menu-item-title'],	'post_type' => 'nav_menu_item',	);	$post_date = wp_resolve_post_date( $args['menu-item-post-date'], $args['menu-item-post-date-gmt'] );	if ( $post_date ) {	$post['post_date'] = $post_date;	}	$update = 0 !== $menu_item_db_id;	// New menu item. Default is draft status.	if ( ! $update ) {	$post['ID'] = 0;	$post['post_status'] = 'publish' === $args['menu-item-status'] ? 'publish' : 'draft';	$menu_item_db_id = wp_insert_post( $post, true, $fire_after_hooks );	if ( ! $menu_item_db_id || is_wp_error( $menu_item_db_id ) ) {	return $menu_item_db_id;	}	/** * Fires immediately after a new navigation menu item has been added. * * @since 4.4.0 * * @see wp_update_nav_menu_item() * * @param int $menu_id ID of the updated menu. * @param int $menu_item_db_id ID of the new menu item. * @param array $args An array of arguments used to update/add the menu item. */	do_action( 'wp_add_nav_menu_item', $menu_id, $menu_item_db_id, $args );	}	/* * Associate the menu item with the menu term. * Only set the menu term if it isn't set to avoid unnecessary wp_get_object_terms(). */	if ( $menu_id && ( ! $update || ! is_object_in_term( $menu_item_db_id, 'nav_menu', (int) $menu->term_id ) ) ) {	$update_terms = wp_set_object_terms( $menu_item_db_id, array( $menu->term_id ), 'nav_menu' );	if ( is_wp_error( $update_terms ) ) {	return $update_terms;	}	}	if ( 'custom' === $args['menu-item-type'] ) {	$args['menu-item-object-id'] = $menu_item_db_id;	$args['menu-item-object'] = 'custom';	}	$menu_item_db_id = (int) $menu_item_db_id;	// Reset invalid `menu_item_parent`.	if ( (int) $args['menu-item-parent-id'] === $menu_item_db_id ) {	$args['menu-item-parent-id'] = 0;	}	update_post_meta( $menu_item_db_id, '_menu_item_type', sanitize_key( $args['menu-item-type'] ) );	update_post_meta( $menu_item_db_id, '_menu_item_menu_item_parent', (string) ( (int) $args['menu-item-parent-id'] ) );	update_post_meta( $menu_item_db_id, '_menu_item_object_id', (string) ( (int) $args['menu-item-object-id'] ) );	update_post_meta( $menu_item_db_id, '_menu_item_object', sanitize_key( $args['menu-item-object'] ) );	update_post_meta( $menu_item_db_id, '_menu_item_target', sanitize_key( $args['menu-item-target'] ) );	$args['menu-item-classes'] = array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-classes'] ) );	$args['menu-item-xfn'] = implode( ' ', array_map( 'sanitize_html_class', explode( ' ', $args['menu-item-xfn'] ) ) );	update_post_meta( $menu_item_db_id, '_menu_item_classes', $args['menu-item-classes'] );	update_post_meta( $menu_item_db_id, '_menu_item_xfn', $args['menu-item-xfn'] );	update_post_meta( $menu_item_db_id, '_menu_item_url', sanitize_url( $args['menu-item-url'] ) );	if ( 0 === $menu_id ) {	update_post_meta( $menu_item_db_id, '_menu_item_orphaned', (string) time() );	} elseif ( get_post_meta( $menu_item_db_id, '_menu_item_orphaned' ) ) {	delete_post_meta( $menu_item_db_id, '_menu_item_orphaned' );	}	// Update existing menu item. Default is publish status.	if ( $update ) {	$post['ID'] = $menu_item_db_id;	$post['post_status'] = ( 'draft' === $args['menu-item-status'] ) ? 'draft' : 'publish';	$update_post = wp_update_post( $post, true );	if ( is_wp_error( $update_post ) ) {	return $update_post;	}	}	/** * Fires after a navigation menu item has been updated. * * @since 3.0.0 * * @see wp_update_nav_menu_item() * * @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. */	do_action( 'wp_update_nav_menu_item', $menu_id, $menu_item_db_id, $args );	return $menu_item_db_id; } 

Hooks

do_action( ‘wp_add_nav_menu_item’, int $menu_id, int $menu_item_db_id, array $args )

Fires immediately after a new navigation menu item has been added.

do_action( ‘wp_update_nav_menu_item’, int $menu_id, int $menu_item_db_id, array $args )

Fires after a navigation menu item has been updated.

Changelog

VersionDescription
5.9.0Added the $fire_after_hooks parameter.
3.0.0Introduced.

User Contributed Notes

  1. Skip to note 5 content

    The $menu_item_data argument accepts an array of data. Depending upon the type of nav menu item you are attempting to add, it requires different parameters:

    Note that in several cases, menu-item-object must be included, and it must be set to the post type of the nav item you are referencing.

    Page

    $page = get_post(123); // etc wp_update_nav_menu_item($menu_id, 0, array( 'menu-item-title' => 'My Link', 'menu-item-object-id' => $page->ID, 'menu-item-object' => 'page', 'menu-item-status' => 'publish', 'menu-item-type' => 'post_type', ));

    Post

    $post = get_post(123); // etc wp_update_nav_menu_item($menu_id, 0, array( 'menu-item-title' => 'My Link', 'menu-item-object-id' => $post->ID, 'menu-item-object' => 'post', 'menu-item-status' => 'publish', 'menu-item-type' => 'post_type', ));

    Custom

    When adding a custom link to your nav menu, you can omit the menu-item-type because it defaults to custom.

    wp_update_nav_menu_item($menu_id, 0, array( 'menu-item-title' => 'My Link', 'menu-item-url' => 'http://example.com/', 'menu-item-status' => 'publish', 'menu-item-type' => 'custom', // optional ));
  2. Skip to note 6 content

    When adding a link for a custom post type.

    In addition to the other minimum required parameters, pass these values to the $menu_item_data parameter:

    'menu-item-object'	=> 'your-registered-post-type-name', // i.e., foo-bar, foo_bar, 'event-scheduler' 'menu-item-type'	=> 'post_type', // For example: $menu_item_data = [	'menu-item-object-id' => $post->ID, // Or however, you get the post's id.	'menu-item-object' => 'wpdocs-foo-bar',	'menu-item-type' => 'post_type',	'menu-item-status' => 'publish',	'menu-item-title' => esc_html__( 'It\'s all Foo Bar' ),	'menu-item-description' => esc_html__( 'Page about how, It\'s all Foo Bar' ),	'menu-item-classes' => 'wpdocs-foo-bar', ]; wp_update_nav_menu_item( $menu_id, 0, $menu_item_data )
  3. Skip to note 7 content

    Example on how to work with taxonomies: automatically add a new menu item when a new woocommerce product category has been created. Also if it’s a subcategory – add it as a respective subitem.

    // on woocommerce product category creation add it to menu: add_action( 'create_product_cat', 'wpdocs_add_menu_item_on_create_product_cat' ); function wpdocs_add_menu_item_on_create_product_cat( $term_id ) {	// get target menu from menu location:	$menu_ids = get_nav_menu_locations();	$menu_id = $menu_ids['menu-1'];	$args = array(	'menu-item-object-id' => $term_id,	'menu-item-object' => 'product_cat',	'menu-item-type' => 'taxonomy',	'menu-item-status' => 'publish',	);	// also check if current category is a subcategory, and if so,	// if it's parent category has a menu item - then assign current item	// as a subitem to that:	$parent_term_id = get_term_field( 'parent', $term_id, 'product_cat', 'raw' );	if ( $parent_term_id ) {	$parent_term_menu_item = get_posts( array(	'numberposts' => 1,	'post_type' => 'nav_menu_item',	'meta_key' => '_menu_item_object_id',	'meta_value' => $parent_term_id,	'fields' => 'ids'	) );	if ( ! empty( $parent_term_menu_item ) ) {	$args['menu-item-parent-id'] = $parent_term_menu_item[0];	}	}	wp_update_nav_menu_item( $menu_id, 0, $args ); }
  4. Skip to note 8 content

    If you’re updating an existing menu item and want to keep the ordering, pass 'menu-item-position'. Otherwise, the item will move to the end of the menu.

    $menu_item_data = array( 'menu-item-position' => $item->menu_order, 'menu-item-parent-id' => $item->menu_item_parent, 'menu-item-type' => 'custom', 'menu-item-url' => $url, 'menu-item-title' => $item->title, 'menu-item-status' => $item->post_status, );

You must log in before being able to contribute a note or feedback.