From aef77be6274b52391e3065bea15a4da506c755be Mon Sep 17 00:00:00 2001 From: Jonny Harris Date: Wed, 15 Jun 2022 10:18:02 +0000 Subject: [PATCH] REST API: Prime caches for linked objects in menu item REST API controller. Add a new parameter to `WP_Query` called `update_menu_item_cache` that when set to true, primes the caches for linked terms and posts for menu item post objects. This change moves logic found in `wp_get_nav_menu_items` into a new function called `update_menu_item_cache`. Update the menu item REST API controller, to pass the `update_menu_item_cache` parameter to the arguments used for the `WP_Query` run to get menu items. Props furi3r, TimothyBlynJacobs, spacedmonkey, peterwilsoncc, mitogh. Fixes #55620. --This line, and those below, will be ignored-- M src/wp-includes/class-wp-query.php M src/wp-includes/nav-menu.php M src/wp-includes/rest-api/endpoints/class-wp-rest-menu-items-controller.php M tests/phpunit/tests/post/nav-menu.php git-svn-id: https://develop.svn.wordpress.org/trunk@53504 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-query.php | 9 +++ src/wp-includes/nav-menu.php | 80 ++++++++++--------- .../class-wp-rest-menu-items-controller.php | 2 + tests/phpunit/tests/post/nav-menu.php | 61 ++++++++++++++ 4 files changed, 115 insertions(+), 37 deletions(-) diff --git a/src/wp-includes/class-wp-query.php b/src/wp-includes/class-wp-query.php index efa7dc8db3..a0d364976e 100644 --- a/src/wp-includes/class-wp-query.php +++ b/src/wp-includes/class-wp-query.php @@ -756,6 +756,7 @@ class WP_Query { * @type string $title Post title. * @type bool $update_post_meta_cache Whether to update the post meta cache. Default true. * @type bool $update_post_term_cache Whether to update the post term cache. Default true. + * @type bool $update_menu_item_cache Whether to update the menu item cache. Default false. * @type bool $lazy_load_term_meta Whether to lazy-load term meta. Setting to false will * disable cache priming for term meta, so that each * get_term_meta() call will hit the database. @@ -1871,6 +1872,10 @@ class WP_Query { $q['update_post_term_cache'] = true; } + if ( ! isset( $q['update_menu_item_cache'] ) ) { + $q['update_menu_item_cache'] = false; + } + if ( ! isset( $q['lazy_load_term_meta'] ) ) { $q['lazy_load_term_meta'] = $q['update_post_term_cache']; } @@ -3147,6 +3152,10 @@ class WP_Query { $this->posts = array_map( 'get_post', $this->posts ); } + if ( ! empty( $this->posts ) && $q['update_menu_item_cache'] ) { + update_menu_item_cache( $this->posts ); + } + if ( ! $q['suppress_filters'] ) { /** * Filters the raw post results array, prior to status checks. diff --git a/src/wp-includes/nav-menu.php b/src/wp-includes/nav-menu.php index 63969287d7..c018cf23f9 100644 --- a/src/wp-includes/nav-menu.php +++ b/src/wp-includes/nav-menu.php @@ -691,21 +691,20 @@ function wp_get_nav_menu_items( $menu, $args = array() ) { return false; } - static $fetched = array(); - if ( ! taxonomy_exists( 'nav_menu' ) ) { return false; } $defaults = array( - 'order' => 'ASC', - 'orderby' => 'menu_order', - 'post_type' => 'nav_menu_item', - 'post_status' => 'publish', - 'output' => ARRAY_A, - 'output_key' => 'menu_order', - 'nopaging' => true, - 'tax_query' => array( + 'order' => 'ASC', + 'orderby' => 'menu_order', + 'post_type' => 'nav_menu_item', + 'post_status' => 'publish', + 'output' => ARRAY_A, + 'output_key' => 'menu_order', + 'nopaging' => true, + 'update_menu_item_cache' => true, + 'tax_query' => array( array( 'taxonomy' => 'nav_menu', 'field' => 'term_taxonomy_id', @@ -720,33 +719,6 @@ function wp_get_nav_menu_items( $menu, $args = array() ) { $items = array(); } - // Prime posts and terms caches. - if ( empty( $fetched[ $menu->term_id ] ) ) { - $fetched[ $menu->term_id ] = true; - $post_ids = array(); - $term_ids = array(); - foreach ( $items as $item ) { - $object_id = get_post_meta( $item->ID, '_menu_item_object_id', true ); - $type = get_post_meta( $item->ID, '_menu_item_type', true ); - - if ( 'post_type' === $type ) { - $post_ids[] = (int) $object_id; - } elseif ( 'taxonomy' === $type ) { - $term_ids[] = (int) $object_id; - } - } - - if ( ! empty( $post_ids ) ) { - _prime_post_caches( $post_ids, false ); - } - unset( $post_ids ); - - if ( ! empty( $term_ids ) ) { - _prime_term_caches( $term_ids ); - } - unset( $term_ids ); - } - $items = array_map( 'wp_setup_nav_menu_item', $items ); if ( ! is_admin() ) { // Remove invalid items only on front end. @@ -780,6 +752,40 @@ function wp_get_nav_menu_items( $menu, $args = array() ) { return apply_filters( 'wp_get_nav_menu_items', $items, $menu, $args ); } +/** + * Prime all linked objects to menu items. + * + * @since 6.1.0 + * + * @param WP_Post[] $menu_items Array post objects of menu items. + */ +function update_menu_item_cache( $menu_items ) { + $post_ids = array(); + $term_ids = array(); + + foreach ( $menu_items as $menu_item ) { + if ( 'nav_menu_item' !== $menu_item->post_type ) { + continue; + } + $object_id = get_post_meta( $menu_item->ID, '_menu_item_object_id', true ); + $type = get_post_meta( $menu_item->ID, '_menu_item_type', true ); + + if ( 'post_type' === $type ) { + $post_ids[] = (int) $object_id; + } elseif ( 'taxonomy' === $type ) { + $term_ids[] = (int) $object_id; + } + } + + if ( ! empty( $post_ids ) ) { + _prime_post_caches( $post_ids, false ); + } + + if ( ! empty( $term_ids ) ) { + _prime_term_caches( $term_ids ); + } +} + /** * Decorates a menu item object with the shared navigation menu item properties. * diff --git a/src/wp-includes/rest-api/endpoints/class-wp-rest-menu-items-controller.php b/src/wp-includes/rest-api/endpoints/class-wp-rest-menu-items-controller.php index 825833505d..8b57909169 100644 --- a/src/wp-includes/rest-api/endpoints/class-wp-rest-menu-items-controller.php +++ b/src/wp-includes/rest-api/endpoints/class-wp-rest-menu-items-controller.php @@ -998,6 +998,8 @@ class WP_REST_Menu_Items_Controller extends WP_REST_Posts_Controller { } } + $query_args['update_menu_item_cache'] = true; + return $query_args; } diff --git a/tests/phpunit/tests/post/nav-menu.php b/tests/phpunit/tests/post/nav-menu.php index c95c6709c4..02ee4cfe4a 100644 --- a/tests/phpunit/tests/post/nav-menu.php +++ b/tests/phpunit/tests/post/nav-menu.php @@ -202,6 +202,67 @@ class Tests_Post_Nav_Menu extends WP_UnitTestCase { $this->assertEquals( $t, $menu_items[0]->object_id ); } + /** + * @ticket 55620 + */ + public function test_update_menu_item_cache_primed_post() { + $post_id = self::factory()->post->create(); + wp_update_nav_menu_item( + $this->menu_id, + 0, + array( + 'menu-item-type' => 'post_type', + 'menu-item-object' => 'post', + 'menu-item-object-id' => $post_id, + 'menu-item-status' => 'publish', + ) + ); + + $posts_query = new WP_Query(); + $query_result = $posts_query->query( array( 'post_type' => 'nav_menu_item' ) ); + + wp_cache_delete( $post_id, 'posts' ); + $action = new MockAction(); + add_filter( 'update_post_metadata_cache', array( $action, 'filter' ), 10, 2 ); + + update_menu_item_cache( $query_result ); + + $args = $action->get_args(); + $last = end( $args ); + $this->assertEqualSets( array( $post_id ), $last[1], '_prime_post_caches was not executed.' ); + } + + /** + * @ticket 55620 + */ + public function test_update_menu_item_cache_primed_terms() { + register_taxonomy( 'wptests_tax', 'post', array( 'hierarchical' => true ) ); + $term_id = self::factory()->term->create( array( 'taxonomy' => 'wptests_tax' ) ); + wp_update_nav_menu_item( + $this->menu_id, + 0, + array( + 'menu-item-type' => 'taxonomy', + 'menu-item-object' => 'wptests_tax', + 'menu-item-object-id' => $term_id, + 'menu-item-status' => 'publish', + ) + ); + + $posts_query = new WP_Query(); + $query_result = $posts_query->query( array( 'post_type' => 'nav_menu_item' ) ); + + wp_cache_delete( $term_id, 'terms' ); + $action = new MockAction(); + add_filter( 'update_term_metadata_cache', array( $action, 'filter' ), 10, 2 ); + + update_menu_item_cache( $query_result ); + + $args = $action->get_args(); + $last = end( $args ); + $this->assertEqualSets( array( $term_id ), $last[1], '_prime_term_caches was not executed.' ); + } + /** * @ticket 13910 */