Skip to content

Commit b940b5d

Browse files
authored
Merge pull request #275 from petruchek/wp-cli-issue-5047
Recalculate menu order on insertion or deletion
2 parents 0c141d9 + 3a3b2a4 commit b940b5d

File tree

3 files changed

+110
-16
lines changed

3 files changed

+110
-16
lines changed

composer.json

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,10 @@
2424
},
2525
"config": {
2626
"process-timeout": 7200,
27-
"sort-packages": true
27+
"sort-packages": true,
28+
"allow-plugins": {
29+
"dealerdirect/phpcodesniffer-composer-installer": true
30+
}
2831
},
2932
"extra": {
3033
"branch-alias": {

features/menu-item.feature

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ Feature: Manage WordPress menu items
3434
When I run `wp menu item add-term sidebar-menu post_tag {TERM_ID} --porcelain`
3535
Then save STDOUT as {TERM_ITEM_ID}
3636

37-
When I run `wp menu item add-custom sidebar-menu Apple http://apple.com --parent-id={POST_ITEM_ID} --porcelain`
37+
When I run `wp menu item add-custom sidebar-menu Apple https://apple.com --parent-id={POST_ITEM_ID} --porcelain`
3838
Then save STDOUT as {CUSTOM_ITEM_ID}
3939

40-
When I run `wp menu item update {CUSTOM_ITEM_ID} --title=WordPress --link='http://wordpress.org' --target=_blank --position=2`
40+
When I run `wp menu item update {CUSTOM_ITEM_ID} --title=WordPress --link='https://wordpress.org' --target=_blank --position=2`
4141
Then STDOUT should be:
4242
"""
4343
Success: Menu item updated.
@@ -51,10 +51,10 @@ Feature: Manage WordPress menu items
5151

5252
When I run `wp menu item list sidebar-menu --fields=type,title,description,position,link,menu_item_parent`
5353
Then STDOUT should be a table containing rows:
54-
| type | title | description | position | link | menu_item_parent |
55-
| post_type | Custom Test Post | Washington Apples | 1 | {POST_LINK} | 0 |
56-
| custom | WordPress | | 2 | http://wordpress.org | {POST_ITEM_ID} |
57-
| taxonomy | Test term | | 3 | {TERM_LINK} | 0 |
54+
| type | title | description | position | link | menu_item_parent |
55+
| post_type | Custom Test Post | Washington Apples | 1 | {POST_LINK} | 0 |
56+
| custom | WordPress | | 2 | https://wordpress.org | {POST_ITEM_ID} |
57+
| taxonomy | Test term | | 3 | {TERM_LINK} | 0 |
5858

5959
When I run `wp menu item list sidebar-menu --format=ids`
6060
Then STDOUT should not be empty
@@ -122,7 +122,7 @@ Feature: Manage WordPress menu items
122122
"""
123123
And the return code should be 1
124124

125-
When I run `wp menu item add-custom sidebar-menu Apple http://apple.com --porcelain`
125+
When I run `wp menu item add-custom sidebar-menu Apple https://apple.com --porcelain`
126126
Then save STDOUT as {CUSTOM_ITEM_ID}
127127

128128
When I try `wp menu item delete {CUSTOM_ITEM_ID} 99999999`
@@ -132,3 +132,66 @@ Feature: Manage WordPress menu items
132132
Error: Only deleted 1 of 2 menu items.
133133
"""
134134
And the return code should be 1
135+
136+
Scenario: Menu order is recalculated on insertion
137+
When I run `wp menu create "Sidebar Menu"`
138+
Then STDOUT should not be empty
139+
140+
When I run `wp menu item add-custom sidebar-menu First https://first.com --porcelain`
141+
Then save STDOUT as {ITEM_ID_1}
142+
143+
When I run `wp menu item add-custom sidebar-menu Second https://second.com --porcelain`
144+
Then save STDOUT as {ITEM_ID_2}
145+
146+
When I run `wp menu item add-custom sidebar-menu Third https://third.com --porcelain`
147+
Then save STDOUT as {ITEM_ID_3}
148+
149+
When I run `wp menu item list sidebar-menu --fields=type,title,position,link`
150+
Then STDOUT should be a table containing rows:
151+
| type | title | position | link |
152+
| custom | First | 1 | https://first.com |
153+
| custom | Second | 2 | https://second.com |
154+
| custom | Third | 3 | https://third.com |
155+
156+
When I run `wp menu item add-custom sidebar-menu Fourth https://fourth.com --position=2 --porcelain`
157+
Then save STDOUT as {ITEM_ID_4}
158+
159+
When I run `wp menu item list sidebar-menu --fields=type,title,position,link`
160+
Then STDOUT should be a table containing rows:
161+
| type | title | position | link |
162+
| custom | First | 1 | https://first.com |
163+
| custom | Fourth | 2 | https://fourth.com |
164+
| custom | Second | 3 | https://second.com |
165+
| custom | Third | 4 | https://third.com |
166+
167+
Scenario: Menu order is recalculated on deletion
168+
When I run `wp menu create "Sidebar Menu"`
169+
Then STDOUT should not be empty
170+
171+
When I run `wp menu item add-custom sidebar-menu First https://first.com --porcelain`
172+
Then save STDOUT as {ITEM_ID_1}
173+
174+
When I run `wp menu item add-custom sidebar-menu Second https://second.com --porcelain`
175+
Then save STDOUT as {ITEM_ID_2}
176+
177+
When I run `wp menu item add-custom sidebar-menu Third https://third.com --porcelain`
178+
Then save STDOUT as {ITEM_ID_3}
179+
180+
When I run `wp menu item list sidebar-menu --fields=type,title,position,link`
181+
Then STDOUT should be a table containing rows:
182+
| type | title | position | link |
183+
| custom | First | 1 | https://first.com |
184+
| custom | Second | 2 | https://second.com |
185+
| custom | Third | 3 | https://third.com |
186+
187+
When I run `wp menu item delete {ITEM_ID_2}`
188+
Then STDOUT should be:
189+
"""
190+
Success: Deleted 1 of 1 menu items.
191+
"""
192+
193+
When I run `wp menu item list sidebar-menu --fields=type,title,position,link`
194+
Then STDOUT should be a table containing rows:
195+
| type | title | position | link |
196+
| custom | First | 1 | https://first.com |
197+
| custom | Third | 2 | https://third.com |

src/Menu_Item_Command.php

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -369,19 +369,28 @@ public function delete( $args, $assoc_args ) {
369369

370370
foreach ( $args as $arg ) {
371371

372+
$post = get_post( $arg );
373+
$menu_term = get_the_terms( $arg, 'nav_menu' );
372374
$parent_menu_id = (int) get_post_meta( $arg, '_menu_item_menu_item_parent', true );
373375
$result = wp_delete_post( $arg, true );
374376
if ( ! $result ) {
375377
WP_CLI::warning( "Couldn't delete menu item {$arg}." );
376378
$errors++;
377-
} elseif ( $parent_menu_id ) {
378-
$children = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_menu_item_menu_item_parent' AND meta_value=%s", (int) $arg ) );
379-
if ( $children ) {
380-
$children_query = $wpdb->prepare( "UPDATE $wpdb->postmeta SET meta_value = %d WHERE meta_key = '_menu_item_menu_item_parent' AND meta_value=%s", $parent_menu_id, (int) $arg );
381-
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $children_query is already prepared above.
382-
$wpdb->query( $children_query );
383-
foreach ( $children as $child ) {
384-
clean_post_cache( $child );
379+
} else {
380+
381+
if ( is_array( $menu_term ) && ! empty( $menu_term ) && $post ) {
382+
$this->reorder_menu_items( $menu_term[0]->term_id, $post->menu_order, -1, 0 );
383+
}
384+
385+
if ( $parent_menu_id ) {
386+
$children = $wpdb->get_results( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key='_menu_item_menu_item_parent' AND meta_value=%s", (int) $arg ) );
387+
if ( $children ) {
388+
$children_query = $wpdb->prepare( "UPDATE $wpdb->postmeta SET meta_value = %d WHERE meta_key = '_menu_item_menu_item_parent' AND meta_value=%s", $parent_menu_id, (int) $arg );
389+
// phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- $children_query is already prepared above.
390+
$wpdb->query( $children_query );
391+
foreach ( $children as $child ) {
392+
clean_post_cache( $child );
393+
}
385394
}
386395
}
387396
}
@@ -477,6 +486,10 @@ private function add_or_update_item( $method, $type, $args, $assoc_args ) {
477486
}
478487
} else {
479488

489+
if ( ( 'add' === $method ) && $menu_item_args['menu-item-position'] ) {
490+
$this->reorder_menu_items( $menu->term_id, $menu_item_args['menu-item-position'], +1, $result );
491+
}
492+
480493
/**
481494
* Set the menu
482495
*
@@ -503,6 +516,21 @@ private function add_or_update_item( $method, $type, $args, $assoc_args ) {
503516

504517
}
505518

519+
/**
520+
* Move block of items in one nav_menu up or down by incrementing/decrementing their menu_order field.
521+
* Expects the menu items to have proper menu_orders (i.e. doesn't fix errors from previous incorrect operations).
522+
*
523+
* @param int $menu_id ID of the nav_menu
524+
* @param int $min_position minimal menu_order to touch
525+
* @param int $increment how much to change menu_order: +1 to move down, -1 to move up
526+
* @param int $ignore_item_id menu item that should be ignored by the change (e.g. newly created menu item)
527+
* @return int number of rows affected
528+
*/
529+
private function reorder_menu_items( $menu_id, $min_position, $increment, $ignore_item_id = 0 ) {
530+
global $wpdb;
531+
return $wpdb->query( $wpdb->prepare( "UPDATE $wpdb->posts SET `menu_order`=`menu_order`+(%d) WHERE `menu_order`>=%d AND ID IN (SELECT object_id FROM $wpdb->term_relationships WHERE term_taxonomy_id=%d) AND ID<>%d", (int) $increment, (int) $min_position, (int) $menu_id, (int) $ignore_item_id ) );
532+
}
533+
506534
protected function get_formatter( &$assoc_args ) {
507535
return new Formatter( $assoc_args, $this->obj_fields );
508536
}

0 commit comments

Comments
 (0)