I've searched for hours and cannot come up with what I thought would be a simple thing. I am building my very first Wordpress site locally. I'm making a function for my functions.php file based on this howto but I need it to have submenus. I have created a custom menu inside Wordpress with 2 levels.
I need to add HTML code (not a CSS class) to an item if it is a parent item.
Bonus points if you can tell me how people know what attributes/properties are available to a menu item (such as title and url) - I can't find a reference to that anywhere!
Here is my current code, which is currently displaying all menu items as top-level items.
function clean_custom_main_menu() {
$menu_name = 'main-menu'; // specify custom menu slug
$menu_list = '<ul id="menu">' ."\n";
if ($menu_items = wp_get_nav_menu_items($menu_name)) {
$count = 0;
$submenu = false;
foreach ((array) $menu_items as $key => $menu_item) {
$title = $menu_item->title;
$url = $menu_item->url;
$classes = $menu_item->classes; // does not work
$has_children = $menu_item->has_children; //does not work
$parent_id = 0;
// check if this item is a parent item with children
//if ( $menu_item->menu_item_parent && $menu_item->menu_item_parent > 0 ) {
//if(in_array('menu-item-has-children', $classes)){
if ($has_children) {
$parent_id = $menu_item->ID;
}
// if this item has a parent ID, it's a second-level item
if ($parent_id != 0 && $parent_id == $menu_item->menu_item_parent ) {
$submenu = true;
}
if (!$has_children) {
// if this item has no submenu, write top-level code
$menu_list .= "\t". '<li>'. $title .'</li>' ."\n";
}
// the "else" is not currently being hit
//else {
// $menu_list .= "\t" . '<li>' . $title . '<span class="arrow-down"></span></li>' . "\n";
// $menu_list .= '<ul class="sub">';
// // foreach (child) do children
// $menu_list .= '</ul>';
// $menu_list .= '</li>';
//}
}
} else {
$menu_list .= '<!-- no list defined -->';
}
$menu_list .= "\t". '</ul>' ."\n";
echo $menu_list;
}
Desired output:
<ul id="menu">
<li>Home</li>
<li>
About <span class="arrow-down"></span>
<ul class="sub">
<li>Our Services</li>
<li>What We Do</li>
</ul>
</li>
<li>
Top-Level Item
</li>
<li>Top-Level Item with Children <span class="arrow-down"></span>
<ul class="sub">
<li>Sub Item 1</li>
<li>Sub Item 2</li>
<li>Sub Item 3</li>
<li>Sub Item 4</li>
</ul>
</li>
<li>Contact</li>
</ul>
Please note: I do not want to use walkers. I am certain it can be done somehow since I had this working at one point, but lost everything when Wordpress decided to update the theme I made - to a totally different one from web-land, erasing all my files. D'oh!
It turns out that people have contributed how to build items with submenus right on the Wordpress documentation for the function I was using, wp_get_nav_menu_items(). Those got me started but they weren't exactly quite right, since I had the extra span tags and wanted to close my A tags in different places, but I was able to ultimately get things working. Here is my final code based on those comments:
function clean_custom_main_menu() {
$menu_name = 'main-menu'; // specify custom menu slug
$menu_list = '<ul id="menu">' ."\n";
if ($menu_items = wp_get_nav_menu_items($menu_name)) {
$count = 0;
$submenu = false;
$parent_id = 0;
$previous_item_has_submenu = false;
foreach ((array) $menu_items as $key => $menu_item) {
$title = $menu_item->title;
$url = $menu_item->url;
// check if it's a top-level item
if ($menu_item->menu_item_parent == 0) {
$parent_id = $menu_item->ID;
// write the item but DON'T close the A or LI until we know if it has children!
$menu_list .= "\t". '<li><a href="'. $url .'">'. $title;
}
// if this item has a (nonzero) parent ID, it's a second-level (child) item
else {
if ( !$submenu ) { // first item
// add the dropdown arrow to the parent
$menu_list .= '<span class="arrow-down"></span></a>' . "\n";
// start the child list
$submenu = true;
$previous_item_has_submenu = true;
$menu_list .= "\t\t" . '<ul class="sub">' ."\n";
}
$menu_list .= "\t\t\t" . '<li>';
$menu_list .= ''.$title.'';
$menu_list .= '</li>' ."\n";
// if it's the last child, close the submenu code
if ( $menu_items[ $count + 1 ]->menu_item_parent != $parent_id && $submenu ){
$menu_list .= "\t\t" . '</ul></li>' ."\n";
$submenu = false;
}
}
// close the parent (top-level) item
if (empty($menu_items[$count + 1]) || $menu_items[ $count + 1 ]->menu_item_parent != $parent_id )
{
if ($previous_item_has_submenu)
{
// the a link and list item were already closed
$previous_item_has_submenu = false; //reset
}
else {
// close a link and list item
$menu_list .= "\t" . '</a></li>' . "\n";
}
}
$count++;
}
} else {
$menu_list .= '<!-- no list defined -->';
}
$menu_list .= "\t". '</ul>' ."\n";
echo $menu_list;
}
API
Try doing var_dump( $menu_items ) and you'll see that the menu items are posts of post_type "nav_menu_item" so they have the same api as post.
Count Children
https://wordpress.stackexchange.com/questions/9374/get-the-post-children-count-of-a-post shows a way of accessing child post count using $wpdb. You could do this with get_posts by setting the parent_id to the parent item and post_type to nav_menu_item and then count the results.
Classes
The post object has a classes array. Since this wasn't working for you, I'd double check how you are trying to access/use that array and if that doesn't help, try investigating the walker functions for help with your custom implementation.
Related
I need the parent element's title as a variable and also the submenu of it.
Currently I have:
function navigation_footer() {
$menu_name = 'footer_nav';
if (($locations = get_nav_menu_locations()) && isset($locations[$menu_name])) {
$menu = wp_get_nav_menu_object($locations[$menu_name]);
$menu_items = wp_get_nav_menu_items($menu->term_id);
foreach ((array) $menu_items as $key => $menu_item) {
$title = $menu_item->title;
$url = $menu_item->url;
$type = get_post_meta( $item->menu_item_parent, '_menu_item_type', true );
$title_parent = get_post( $object_id )->post_title;
$menu_list .= "\t\t\t\t". '<div class="fl-links"><h2>'. $title_parent .'</h2>' ."\n";
$menu_list .= "\t\t\t\t\t". '<p>'. $title .'</p></div>' ."\n";
}
}
echo $menu_list;
}
The variable type gets me the title of a post which was kinda workaround.
Still, it's in a foreach so I'm getting a mess instead of what I actually want.
As I'm a fairly beginner in PHP I am stuck.
Each element in WP is going to be an individual link. The parent is only being used as a placeholder for the title followed by the links.
[Current output](https://i.stack.imgur.com/xsGRS.png)
[My desired output](https://i.stack.imgur.com/SP14p.png)
[WP Menu Parent/Child](https://i.stack.imgur.com/Z5d8K.png)
I am trying to output Wordpress' navigation menu items along with their CSS Classes.
I was able to get the menu items, but the classes are more complicated. the css classes come in an array but it's already inside a foreach() loop.
$menu_items = wp_get_nav_menu_items($menu->term_id);
$menu_list = '<ul id="menu-' . $menu_name . '">';
foreach ( (array) $menu_items as $key => $menu_item ) {
$title = $menu_item->title;
$url = $menu_item->url;
$classes = $menu_item->classes;
$menu_list .= '<li>';
$menu_list .= '<i class=""></i>';
$menu_list .= '' . $title . '';
$menu_list .= '</li>';
}
$menu_list .= '</ul>';
As you can see, inside the foreach() loop I am getting the $title and $url which are both strings. But then when I try to get the css classes associated with the menu item through $menu_item->classes it returns an array and I am not sure how to do this.
Because each menu item has 3 css classses fa fa-user fa-3x I can return the values using this
$classes[0] . $classes[1] . $classes[2]
But suppose I don't want the third class and I leave it out empty, I get a missing offset error.
I also tried doing a foreach within the original foreach
$menu_list .= '<li>';
foreach($classes as $class) {
$menu_list .= '<i class="'. $class .'"></i>';
}
$menu_list .= '' . $title . '';
$menu_list .= '</li>';
But this returns 3 separate <i> tags for each menu item, which is not what I want. The output looks like this
<i class="fa"></i>
<i class="fa-user"></i>
<i class="fa-3x"></i>
Simply use a nested Loop in place like so:
<?php
$menu_items = wp_get_nav_menu_items($menu->term_id);
$menu_list = '<ul id="menu-' . $menu_name . '">';
foreach ( (array) $menu_items as $key => $menu_item ) {
$title = $menu_item->title;
$url = $menu_item->url;
$classes = $menu_item->classes;
$comboClass = "";
// USING NESTED LOOP CONSTRUCT
foreach($classes as $class){
$comboClass .= $class . " "; // SEPARATE CLASS-NAME WITH A SPACE
}
$menu_list .= '<li>';
$menu_list .= '<i class="' .$comboClass . '"></i>';
$menu_list .= '' . $title . '';
$menu_list .= '</li>';
}
$menu_list .= '</ul>';
You can nest loops...
foreach(...) {
... html stuff ..
echo '<div class="';
foreach($classes as $class) {
echo $class, ' ';
}
echo '">...</div>';
}
I want to create a drop down menu on first it shows main categories then all child categories with all of their sub child list like the one shown in below pic desired drop down menu
I am using a readymade hiearchical database. my databse structure is like my database structure
here is my false approach categoriesTest.php
<?php
include 'common.php';
$query = "SELECT cat_id, parent_id, cat_name FROM " .$DBPrefix. "categories ORDER BY cat_name";
$res = mysql_query($query);
$system->check_mysql($res, $query, __LINE__, __FILE__);
$items = mysql_fetch_assoc($res);
$html = '';
$parent = 0;
$parent_stack = array();
// $items contains the results of the SQL query
$children = array();
foreach ( $items as $item )
$children[$items['parent_id']][] = $item;
while ( $option = each( $children[$parent] ) )
{
if ( !empty( $option ) )
{
// 1) The item contains children:
// store current parent in the stack, and update current parent
if ( !empty( $children[$option['value']['id']] ) )
{
$html .= '<li>' . $option['value']['title'] . '</li>';
$html .= '<ul>';
array_push( $parent_stack, $parent );
$parent = $option['value']['id'];
}
// 2) The item does not contain children
else
$html .= '<li>' . $option['value']['title'] . '</li>';
}
// 3) Current parent has no more children:
// jump back to the previous menu level
else
{
$html .= '</ul>';
$parent = array_pop( $parent_stack );
}
}
// At this point, the HTML is already built
echo $html;
?>
the above code does not do the job.
I am quiet newbie and cant figure out.
tried all stacked questions but no success
can anyone suggest me the correct php code?
I have this code that produces a horizontal menu in wordpress. However, it produces invalid code because the html tags aren't closed. I have tried adding the closing tags in the loop but seems to mess up the menu bad.
foreach ( (array) $menu_items as $key => $menu_item ) {
$tiny_menu_list .= '<option value="'. $menu_item->url .'">'. $menu_item->title .'</option>';
if( !$menu_item->menu_item_parent ){
$menu_list .= '<li><div>' . $menu_item->title . '';
if( count($menu_items) > 1 )
$menu_list .= '<span>';
continue;
}
$menu_list .= '|' . $menu_item->title . '';
}
if( count($menu_items) > 1 )
$menu_list .= '</span>';
$menu_list .= '</div></li></ul>';
Here is what the code generates:
<li><div>Home Page
<span><li><div>About Us
<span>|Why Us ?|Compare Us
<li><div>Take A Tour
<span>|Frequently Asked Questions
<li><div>Free Options Videos
<span><li><div>Options Courses
<span>|Extended Options Course|Ultimate Options Course</span></span></div></li></ul>
As you can tell, each li & span & div are not closed. Thankfully browsers correct for this, but for the sake of being html5 compliant, could use the help in fixing it.
This is not due to html tags, this is happening because you are missing many closing </div> and </li> .
On formatting the code dumped by your loop it shows up like this:
<li>
<div>
Home Page</li>
<span>
<li>
<div>
About Us
<span>|Why Us ?|Compare Us
<li>
<div>
Take A Tour
<span>|Frequently Asked Questions
<li>
<div>
Free Options Videos
<span>
<li><div>Options Courses
<span>|Extended Options Course|Ultimate Options Course</span></span></div></li></ul>
You can check the error by pasting this code in html validator like this one
And it will show you the missing elements.
Maybe, you can try close your tags in the foreach-loop. Maybe like this:
$hasChildLinks = false
foreach ( (array) $menu_items as $key => $menu_item ) {
$tiny_menu_list .= '<option value="'. $menu_item->url .'">'. $menu_item->title .'</option>';
if( !$menu_item->menu_item_parent ){
$menu_list .= '<li><div>' . $menu_item->title . '';
if( count($menu_items) > 1 ) {
$menu_list .= '<span>'
$hasChildLinks = true;
} else {
$menu_list .= '</div></li>';
}
continue;
}
$menu_list .= '|' . $menu_item->title . '';
if( (count($menu_items) > 1) && ($hasChildLinks) ) {
$menu_list.= '</span>';
}
}
$menu_list .= '</ul>';
I am using the get_categories() function to manually create myself a nav menu. I have a custom taxonomy I'm using called Category and I'm trying to return the link for it for my tags in the menu using the get_category_link() function.
foreach ($categories as $category) {
if ($category->parent == 0) { //Check to see it is a parent
$output .= '<li>';
$output .= '' . $category->name . ''; //display parent taxonomy category
}
}
But it always returns <a href="">. I can echo out the $category->cat_ID successfully so I know it is passing the ID into the function but I don't know why it's returning blank.
Am I missing something? Is it because these are custom taxonomies? They have slugs.
You need something like this for custom taxonomies:
$tax = 'cars';
$cats = get_terms( $tax, '' );
if ($cats) {
foreach($cats as $cat) {
$output .= "<li>";
$output .= '<a href="' . esc_attr(get_term_link($cat, $tax)) . '" title="' . sprintf( __( "View all posts in %s" ), $cat->name ) . '" ' . '>' . $cat->name.'</a>';
$output .= "</li>";
}
}
Although you can easily add to the top of the script to get an array of all taxonomies to feed in if you wanted.