@@ -634,7 +634,71 @@ private function add_or_update_item( $method, $type, $args, $assoc_args ) {
634634 }
635635
636636 $ menu_item_args ['menu-item-type ' ] = $ type ;
637- $ result = wp_update_nav_menu_item ( $ menu ->term_id , $ menu_item_db_id , $ menu_item_args );
637+ $ pending_menu_order_updates = [];
638+
639+ // Reorder other menu items when the position changes on update.
640+ if ( 'update ' === $ method ) {
641+ $ new_position = (int ) $ menu_item_args ['menu-item-position ' ];
642+ if ( $ new_position > 0 ) {
643+ // Fetch all menu items sorted by their raw menu_order to determine
644+ // normalized (1-indexed) ranks, since wp_get_nav_menu_items(ARRAY_A)
645+ // normalises menu_order to 1,2,3… which may differ from the raw DB values.
646+ $ sorted_item_ids = get_posts (
647+ [
648+ 'post_type ' => 'nav_menu_item ' ,
649+ 'numberposts ' => -1 ,
650+ 'orderby ' => 'menu_order ' ,
651+ 'order ' => 'ASC ' ,
652+ 'post_status ' => 'any ' ,
653+ 'tax_query ' => [
654+ [
655+ 'taxonomy ' => 'nav_menu ' ,
656+ 'field ' => 'term_taxonomy_id ' ,
657+ 'terms ' => $ menu ->term_taxonomy_id ,
658+ ],
659+ ],
660+ 'fields ' => 'ids ' ,
661+ ]
662+ );
663+
664+ // Normalise to integers so that strict comparisons below work regardless of
665+ // whether $wpdb->get_col() returned strings or integers.
666+ $ sorted_item_ids = array_map ( 'intval ' , $ sorted_item_ids );
667+
668+ // Clamp the requested position to the valid range of menu items.
669+ $ max_position = count ( $ sorted_item_ids );
670+ if ( $ max_position > 0 && $ new_position > $ max_position ) {
671+ // Treat out-of-range positions as "move to end", consistent with core behavior.
672+ $ new_position = $ max_position ;
673+ }
674+
675+ // Find the 1-indexed normalized rank of the item being moved.
676+ $ item_idx = array_search ( (int ) $ menu_item_db_id , $ sorted_item_ids , true );
677+ $ old_position_normalized = ( false !== $ item_idx ) ? $ item_idx + 1 : 0 ;
678+
679+ if ( $ old_position_normalized > 0 && $ new_position !== $ old_position_normalized ) {
680+ if ( $ new_position < $ old_position_normalized ) {
681+ // Moving up: items at 0-indexed [new_pos-1, old_pos-2] shift down by +1.
682+ for ( $ i = $ new_position - 1 ; $ i <= $ old_position_normalized - 2 ; $ i ++ ) {
683+ $ pending_menu_order_updates [] = [
684+ 'ID ' => $ sorted_item_ids [ $ i ],
685+ 'menu_order ' => $ i + 2 ,
686+ ];
687+ }
688+ } else {
689+ // Moving down: items at 0-indexed [old_pos, new_pos-1] shift up by -1.
690+ for ( $ i = $ old_position_normalized ; $ i <= $ new_position - 1 ; $ i ++ ) {
691+ $ pending_menu_order_updates [] = [
692+ 'ID ' => $ sorted_item_ids [ $ i ],
693+ 'menu_order ' => $ i ,
694+ ];
695+ }
696+ }
697+ }
698+ }
699+ }
700+
701+ $ result = wp_update_nav_menu_item ( $ menu ->term_id , $ menu_item_db_id , $ menu_item_args );
638702
639703 if ( is_wp_error ( $ result ) ) {
640704 WP_CLI ::error ( $ result ->get_error_message () );
@@ -645,6 +709,26 @@ private function add_or_update_item( $method, $type, $args, $assoc_args ) {
645709 WP_CLI ::error ( "Couldn't update menu item. " );
646710 }
647711 } else {
712+ // Apply deferred reordering of other menu items only after a successful update.
713+ if ( ! empty ( $ pending_menu_order_updates ) ) {
714+ global $ wpdb ;
715+
716+ $ ids_to_update = [];
717+ $ case_clauses = '' ;
718+ foreach ( $ pending_menu_order_updates as $ update_args ) {
719+ $ item_id = (int ) $ update_args ['ID ' ];
720+ $ ids_to_update [] = $ item_id ;
721+ $ case_clauses .= $ wpdb ->prepare ( ' WHEN %d THEN %d ' , $ item_id , $ update_args ['menu_order ' ] );
722+ }
723+
724+ $ ids_sql = implode ( ', ' , $ ids_to_update );
725+ // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- $case_clauses and $ids_sql are constructed from prepared/safe integer values.
726+ $ wpdb ->query ( "UPDATE {$ wpdb ->posts } SET menu_order = CASE ID {$ case_clauses } END WHERE ID IN ( {$ ids_sql }) " );
727+
728+ foreach ( $ ids_to_update as $ id ) {
729+ clean_post_cache ( $ id );
730+ }
731+ }
648732
649733 if ( ( 'add ' === $ method ) && $ menu_item_args ['menu-item-position ' ] ) {
650734 $ this ->reorder_menu_items ( $ menu ->term_id , $ menu_item_args ['menu-item-position ' ], +1 , $ result );
0 commit comments