Get WordPress Menu Items
August 7, 2025 · AI Conversations
Here’s a simple example you can run inside a custom plugin or within a theme’s `functions.php` to retrieve and inspect all admin menu items:
—
### ✅ Example: Get All Admin Menu Items
add_action('admin_menu', 'inspect_admin_menu', 999); // Run after all menus are registered
function inspect_admin_menu() {
global $menu, $submenu;
echo '<pre>';
echo "Main Menu:n";
print_r($menu); // Main admin menu
echo "nSubmenu:n";
print_r($submenu); // Submenus under each top-level menu
echo '</pre>';
}
> 🔒 Note: This will output the result in the admin screen, usually before the actual content, which can break layout — use for debugging only.
—
### 📦 Programmatic Access to Menu Items
Each item in `$menu` is an array like:
[
5 => [
0 => 'Posts',
1 => 'edit_posts',
2 => 'edit.php',
3 => '',
4 => 'menu-top menu-icon-post open-if-no-js menu-top-first',
5 => 'menu-posts',
6 => 'dashicons-admin-post'
],
...
]
The keys mean:
– `[0]`: Menu title
– `[1]`: Capability
– `[2]`: Menu slug (URL path)
– `[6]`: Icon (if exists)
The `$submenu` array is indexed by the top-level menu slug (`edit.php`, `upload.php`, etc.) and contains sub-items.
—
### ✅ Example: Loop Through All Menu Items
add_action('admin_menu', 'list_all_menu_items', 999);
function list_all_menu_items() {
global $menu, $submenu;
echo '<pre>';
foreach ($menu as $item) {
$slug = $item[2];
echo "Top Menu: {$item[0]} ({$slug})n";
if (isset($submenu[$slug])) {
foreach ($submenu[$slug] as $subitem) {
echo " → Submenu: {$subitem[0]} ({$subitem[2]})n";
}
}
}
echo '</pre>';
}
—
### 🧪 For Programmatic Use (No Output)
If
—
### ✅ Get "Appearance > Menus" Menu Slug Programmatically
add_action('admin_menu', function () {
global $submenu;
// 'themes.php' is the slug for the Appearance menu
if (isset($submenu['themes.php'])) {
foreach ($submenu['themes.php'] as $item) {
if (stripos($item[0], 'menus') !== false) {
echo '<pre>';
echo "Label: " . esc_html($item[0]) . "n";
echo "Capability: " . esc_html($item[1]) . "n";
echo "Slug: " . esc_html($item[2]) . "n"; // This is the URL part
echo '</pre>';
}
}
}
}, 999);
—
### 🔎 Typical Output:
Label: Menus
Capability: edit_theme_options
Slug: nav-menus.php
So the **slug** for `Appearance > Menus` is:
nav-menus.php
You can use this slug to:
– Build URLs: `admin_url('nav-menus.php')`
– Check current page: `if ($_GET['page'] === 'nav-menus.php')`
– Add submenu after it: `add_submenu_page('themes.php', …)`
—
### 📌 Summary:
– Parent Menu: `themes.php` (Appearance)
– Submenu Title: `Menus`
– Submenu Slug: `nav-menus.php`
Let me know if you want to **add**, **modify**, or **hide** this menu as well.
—
## ✅ Basic Usage of `wp_nav_menu()`
<?php
wp_nav_menu([
'theme_location' => 'primary',
]);
?>
But this requires that the **menu location** `'primary'` is **registered** in your theme.
—
## 🧩 Step-by-Step Implementation
### 1. ✅ Register the Menu Location (usually in `functions.php`)
function register_my_menus() {
register_nav_menus([
'primary' => __('Primary Menu'),
'footer' => __('Footer Menu'),
]);
}
add_action('after_setup_theme', 'register_my_menus');
—
### 2. ✅ Assign Menu to a Location
Go to **Appearance > Menus** in the dashboard, and assign a menu to `Primary Menu` (or whichever you registered).
—
### 3. ✅ Display the Menu in Theme Template
<?php
wp_nav_menu([
'theme_location' => 'primary',
'container' => 'nav', // Wraps the menu in <nav> by default
'container_class' => 'main-nav', // Adds a class to <nav>
'menu_class' => 'menu', // Adds a class to the <ul>
'fallback_cb' => false, // Disable fallback
]);
?>
—
## 📌 Display Menu by Menu Name or ID (Optional)
If you want to load a menu **directly by its name or ID**, do this:
<?php
wp_nav_menu([
'menu' => 'Main Menu', // or menu ID
]);
?>
—
### 🧪 Example HTML Output (Simplified):
<nav class="main-nav">
<ul class="menu">
<li class="menu-item"><a href="/">Home</a></li>
<li class="menu-item"><a href="/about">About</a></li>
...
</ul>
</nav>
—
Let me know if you want to customize the menu output (like dropdowns, active class, mobile menu support, etc.).
'menu' => 20, // or menu ID
]);
print_r($menu);
die;
But i want just menu items not html or anything
The `wp_nav_menu()` function **returns HTML**, not the raw data. To get **just the menu items**, you should use the lower-level function: `wp_get_nav_menu_items()`.
—
## ✅ Use `wp_get_nav_menu_items()` for Raw Menu Data
$menu_items = wp_get_nav_menu_items(20); // Use menu ID or slug
echo '<pre>';
print_r($menu_items); // This will show all menu item objects
echo '</pre>';
die;
—
### 📌 Output Format
Each item is a `WP_Post` object with properties like:
[0] => WP_Post Object (
[ID] => 45
[title] => Home
[menu_order] => 1
[post_name] => home
[url] => https://example.com/
[menu_item_parent] => 0
[type] => custom
[object] => custom
[object_id] => 0
[target] =>
[classes] => Array ( [0] => )
...
)
—
## 🧠 If You Want Just Titles and URLs
$menu_items = wp_get_nav_menu_items(20);
$menu_data = [];
foreach ($menu_items as $item) {
$menu_data[] = [
'title' => $item->title,
'url' => $item->url,
'parent_id' => $item->menu_item_parent,
];
}
echo '<pre>';
print_r($menu_data);
echo '</pre>';
—
Let me know if you want to structure this as a nested tree (parent-child relationship).
Array
(
[0] => Array
(
[title] => Gidsen
[url] => https://professor.nl/?cat=1126
[parent_id] => 0
[catid] => 1126
)
)
– Your code to fetch menu items works in **backend (admin)**.
– But in **frontend**, it only returns **one item (the last one)** in the array.
This typically happens due to **caching**, **variable overwriting**, or incorrect scope when building arrays in a loop.
Let me walk you through a clean, correct way to do it.
—
## ✅ Correct Way to Fetch All Menu Items (Frontend Safe)
$menu_items = wp_get_nav_menu_items(20); // Replace with menu ID or slug
$menu_data = [];
if ($menu_items) {
foreach ($menu_items as $item) {
// Example: extracting custom category ID from the URL if needed
$cat_id = 0;
if (preg_match('/cat=([0-9]+)/', $item->url, $matches)) {
$cat_id = $matches[1];
}
$menu_data[] = [
'title' => $item->title,
'url' => $item->url,
'parent_id' => $item->menu_item_parent,
'catid' => $cat_id,
];
}
}
echo '<pre>';
print_r($menu_data);
echo '</pre>';
—
## ⚠️ Common Issues That Can Cause Only One Item
1. **Overwriting the array in the loop** instead of appending:
$menu_data = [...]; // overwrites each time ❌
Use `[] = …` to append instead ✅
2. **Code inside conditional scope (like `if`, `foreach`)** but outside viewable template
3. **Conflicts with filters/hooks or global scope misuse**
—
## ✅ Quick Debug Checklist
– Confirm `$menu_items` contains more than one item by doing:
var_dump(count($menu_items));
– Make sure `foreach` is **not overwritten** by assigning instead of appending
– Try `wp_get_nav_menu_items('main-menu-slug')` instead of ID to rule out mismatches
—
If you can share the exact code used on the frontend, I can pinpoint it precisely. Want me to show how to convert this into a JSON API as well?
$categoriesallow=[1252,9,17,1240,1068,35,37,38,1177,1234,1229,40,785,1176,757,1160,1052];
$parent_cats = get_terms([
'taxonomy' => 'product_cat',
'parent' => 0,
'hide_empty' => true,
'orderby' => 'name',
'order' => 'ASC',
'includes'=>$categoriesallow
]);
this order categories array
but I want category will show in order of categoriesallow
Let's fix it!
—
## ✅ Step-by-Step Fix
### 1. Correct `get_terms()` parameters:
There is **no `includes`** parameter. It should be `'include'` (singular).
$categoriesallow = [1252,9,17,1240,1068,35,37,38,1177,1234,1229,40,785,1176,757,1160,1052];
$parent_cats = get_terms([
'taxonomy' => 'product_cat',
'parent' => 0,
'hide_empty' => true,
'include' => $categoriesallow,
'orderby' => 'include', // respect the order of $categoriesallow
]);
### 2. ✅ Bonus: Force Custom Sorting (if needed)
Sometimes `'orderby' => 'include'` doesn't work consistently. Here's a manual way to sort terms by `$categoriesallow` order:
$categoriesallow = [1252,9,17,1240,1068,35,37,38,1177,1234,1229,40,785,1176,757,1160,1052];
$terms = get_terms([
'taxonomy' => 'product_cat',
'include' => $categoriesallow,
'hide_empty' => true,
]);
// Reorder to match $categoriesallow
$ordered_terms = [];
$terms_by_id = [];
foreach ($terms as $term) {
$terms_by_id[$term->term_id] = $term;
}
foreach ($categoriesallow as $id) {
if (isset($terms_by_id[$id])) {
$ordered_terms[] = $terms_by_id[$id];
}
}
—
## ✅ Output
Now `$ordered_terms` contains the category terms in the **exact order** defined in `$categoriesallow`.
Use it like:
foreach ($ordered_terms as $cat) {
echo $cat->name . '<br>';
}
—
Let me know if you want to include subcategories or build a nested category tree from this.