$conf, $runtime; function_exists('chdir') AND chdir(APP_PATH); $r = 'mysql' == $conf['cache']['type'] ? website_set('runtime', $runtime) : cache_set('runtime', $runtime); } function runtime_truncate() { global $conf; 'mysql' == $conf['cache']['type'] ? website_set('runtime', '') : cache_delete('runtime'); } register_shutdown_function('runtime_save'); ?>Extend WordPress REST API to show slugs for menu items|Programmer puzzle solving
最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

Extend WordPress REST API to show slugs for menu items

matteradmin7PV0评论

I'm using React to build the front-end of a site which uses Wordpress for the back-end. In particular, I'm using the Wordpress REST API to pull in and display content.

By default, the Wordpress REST API doesn't expose menu information, so I've extended it to include it:

function get_menu( $data ) {
  return wp_get_nav_menu_items( $data[ 'slug' ] );
}

add_action( 'rest_api_init', function () {

  register_rest_route( 'custom', '/menus/(?P<slug>[a-zA-Z0-9-]+)', 
    array(
      'methods'  => 'GET',
      'callback' => 'get_menu'
    )
  );

});

Now I can access my menu information with a request like the following:

http://localhost:8888/learn-react/admin/wp-json/custom/menus/main-menu

The response I get for each of the items included in the menu looks like this:

ID: 13
attr_title: ""
classes: [""]
comment_count: "0"
comment_status: "closed"
db_id: 13
description: ""
filter: "raw"
guid: "http://localhost:8888/learn-react/admin/?p=13"
menu_item_parent: "0"
menu_order: 3
object: "page"
object_id: "8"
ping_status: "closed"
pinged: ""
post_author: "1"
post_content: " "
post_content_filtered: ""
post_date: "2018-02-10 08:57:52"
post_date_gmt: "2018-02-10 08:57:52"
post_excerpt: ""
post_mime_type: ""
post_modified: "2018-02-10 08:57:52"
post_modified_gmt: "2018-02-10 08:57:52"
post_name: "13"
post_parent: 0
post_password: ""
post_status: "publish"
post_title: ""
post_type: "nav_menu_item"
target: ""
title: "About"
to_ping: ""
type: "post_type"
type_label: "Page"
url: "http://localhost:8888/learn-react/admin/about/"
xfn: ""

My problem is I'm trying to use this data to put a link on each menu item, but since I'm using React Router I don't need the whole url (which is provided in the data above), but only the final slug. Here's my menu component:

class MainMenu extends React.Component {

  componentDidMount() {
    this.props.fetchMainMenu();
  }

  render() {

    let menuItemsArray = store.getState().menuReducer.mainMenuItems;     
    let menuItems = menuItemsArray.map( ( menuItem, index ) => {    

      return( 
        <li key={ index }>
          <Link to={ menuItem.url }>{ menuItem.title }</Link>
        </li>
      )
    })
    return (
      <ul>{ menuItems }</ul>
    )
  }
}

Using menuItem.url generates the following href, of course:

http://localhost:3000/http://localhost:8888/learn-react/admin/about/

while, like I said, I would need something like menuItem.slug to generate:

http://localhost:3000/learn-react/admin/about/

What options do I have?

I'm using React to build the front-end of a site which uses Wordpress for the back-end. In particular, I'm using the Wordpress REST API to pull in and display content.

By default, the Wordpress REST API doesn't expose menu information, so I've extended it to include it:

function get_menu( $data ) {
  return wp_get_nav_menu_items( $data[ 'slug' ] );
}

add_action( 'rest_api_init', function () {

  register_rest_route( 'custom', '/menus/(?P<slug>[a-zA-Z0-9-]+)', 
    array(
      'methods'  => 'GET',
      'callback' => 'get_menu'
    )
  );

});

Now I can access my menu information with a request like the following:

http://localhost:8888/learn-react/admin/wp-json/custom/menus/main-menu

The response I get for each of the items included in the menu looks like this:

ID: 13
attr_title: ""
classes: [""]
comment_count: "0"
comment_status: "closed"
db_id: 13
description: ""
filter: "raw"
guid: "http://localhost:8888/learn-react/admin/?p=13"
menu_item_parent: "0"
menu_order: 3
object: "page"
object_id: "8"
ping_status: "closed"
pinged: ""
post_author: "1"
post_content: " "
post_content_filtered: ""
post_date: "2018-02-10 08:57:52"
post_date_gmt: "2018-02-10 08:57:52"
post_excerpt: ""
post_mime_type: ""
post_modified: "2018-02-10 08:57:52"
post_modified_gmt: "2018-02-10 08:57:52"
post_name: "13"
post_parent: 0
post_password: ""
post_status: "publish"
post_title: ""
post_type: "nav_menu_item"
target: ""
title: "About"
to_ping: ""
type: "post_type"
type_label: "Page"
url: "http://localhost:8888/learn-react/admin/about/"
xfn: ""

My problem is I'm trying to use this data to put a link on each menu item, but since I'm using React Router I don't need the whole url (which is provided in the data above), but only the final slug. Here's my menu component:

class MainMenu extends React.Component {

  componentDidMount() {
    this.props.fetchMainMenu();
  }

  render() {

    let menuItemsArray = store.getState().menuReducer.mainMenuItems;     
    let menuItems = menuItemsArray.map( ( menuItem, index ) => {    

      return( 
        <li key={ index }>
          <Link to={ menuItem.url }>{ menuItem.title }</Link>
        </li>
      )
    })
    return (
      <ul>{ menuItems }</ul>
    )
  }
}

Using menuItem.url generates the following href, of course:

http://localhost:3000/http://localhost:8888/learn-react/admin/about/

while, like I said, I would need something like menuItem.slug to generate:

http://localhost:3000/learn-react/admin/about/

What options do I have?

Share Improve this question edited Feb 16, 2018 at 15:17 Zeth 9282 gold badges13 silver badges43 bronze badges asked Feb 16, 2018 at 9:55 grazdevgrazdev 1,3333 gold badges14 silver badges26 bronze badges 2
  • Option A: filter the output of wp_get_nav_menu_items() to not return full URLs. Option B: remove the http://localhost:8888/ part in your React script. Have you tried either? – swissspidy Commented Feb 16, 2018 at 10:30
  • No, I haven't tried either of the options you suggest, but I was actually hoping for a way to achieve my goal by extending the API and adding a 'slug' parameter to the JSON response I posted above somehow. I realize it's probably more complicated, but it sounds cleaner to me. Otherwise I will try your ideas. – grazdev Commented Feb 16, 2018 at 15:54
Add a comment  | 

1 Answer 1

Reset to default 3

I had the same challenge, and fixed it by adding it to the response. Example:

function get_menu() {
    # Change 'menu' to your own navigation slug.
    $menu_items = wp_get_nav_menu_items('menu');
    foreach($menu_items as $menu_item) {
        // ALTERNATIVE: $slug = get_post_field( 'post_name', $menu_item->object_id );
        $slug = basename( get_permalink($menu_item->object_id) );
        $menu_item->slug = $slug;
    }
    return $menu_items;
}

add_action( 'rest_api_init', function () {
        register_rest_route( 'myroutes', '/menu', array(
        'methods' => 'GET',
        'callback' => 'get_menu',
    ) );
} );
Post a comment

comment list (0)

  1. No comments so far