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 did go through this: https://www.elegantthemes.com/blog/tips-tricks/how-to-create-custom-menu-structures-in-wordpress
I did as per the article above to create sub menu of 3rd level. However, In front end the third level of product category for Woocommerce (green apple) not showing up in the dropdown list.
I also read one of stackoverflow answer which says its not possible to create third-level sub menu. I do believe there's a workaround for this. Please advise me.
Thank you.
Tried as per #Rafin's answer,
If third level placed after all second level, then it shows up but this is not where the sub menus for second level menu (apple) supposed to be.
in the backend, placed after all second level menus
Shows up this way in front end,
But I need it to be arranged this way:
You can use following Walker Class that extends Wordpress Nav Menu
<?php
class ods_bootstrap_navwalker extends Walker_Nav_Menu {
/**
* #see Walker::start_lvl()
* #since 3.0.0
*
* #param string $output Passed by reference. Used to append additional content.
* #param int $depth Depth of page. Used for padding.
*/
public function start_lvl( &$output, $depth = 0, $args = array() ) {
$indent = str_repeat( "\t", $depth );
$output .= "\n$indent<div class=\"drop\"><ul role=\"menu\" class=\" dropdown-menu\">\n";
}
/**
* #see Walker::start_el()
* #since 3.0.0
*
* #param string $output Passed by reference. Used to append additional content.
* #param object $item Menu item data object.
* #param int $depth Depth of menu item. Used for padding.
* #param int $current_page Menu item ID.
* #param object $args
*/
public function start_el( &$output, $item, $depth = 0, $args = array(), $id = 0 ) {
$indent = ( $depth ) ? str_repeat( "\t", $depth ) : '';
/**
* Dividers, Headers or Disabled
* =============================
* Determine whether the item is a Divider, Header, Disabled or regular
* menu item. To prevent errors we use the strcasecmp() function to so a
* comparison that is not case sensitive. The strcasecmp() function returns
* a 0 if the strings are equal.
*/
if ( strcasecmp( $item->attr_title, 'divider' ) == 0 && $depth === 1 ) {
$output .= $indent . '<li role="presentation" class="divider">';
} else if ( strcasecmp( $item->title, 'divider') == 0 && $depth === 1 ) {
$output .= $indent . '<li role="presentation" class="divider">';
} else if ( strcasecmp( $item->attr_title, 'dropdown-header') == 0 && $depth === 1 ) {
$output .= $indent . '<li role="presentation" class="dropdown-header">' . esc_attr( $item->title );
} else if ( strcasecmp($item->attr_title, 'disabled' ) == 0 ) {
$output .= $indent . '<li role="presentation" class="disabled">' . esc_attr( $item->title ) . '';
} else {
$class_names = $value = '';
$classes = empty( $item->classes ) ? array() : (array) $item->classes;
$classes[] = 'scroll menu-item-' . $item->ID;
$class_names = join( ' ', apply_filters( 'nav_menu_css_class', array_filter( $classes ), $item, $args ) );
if ( $args->has_children )
$class_names .= ' dropdown';
if ( in_array( 'current-menu-item', $classes ) )
$class_names .= ' active';
$class_names = $class_names ? ' class="' . esc_attr( $class_names ) . '"' : '';
$id = apply_filters( 'nav_menu_item_id', 'menu-item-'. $item->ID, $item, $args );
$id = $id ? ' id="' . esc_attr( $id ) . '"' : '';
$output .= $indent . '<li' . $id . $value . $class_names .'>';
$atts = array();
$atts['title'] = ! empty( $item->title ) ? $item->title : '';
$atts['target'] = ! empty( $item->target ) ? $item->target : '';
$atts['rel'] = ! empty( $item->xfn ) ? $item->xfn : '';
$atts['class'] = "back-top";
// If item has_children add atts to a.
if ( $args->has_children && $depth === 0 ) {
$atts['href'] = '#';
$atts['data-toggle'] = 'dropdown';
$atts['class'] = 'dropdown-toggle';
$atts['aria-haspopup'] = 'true';
} else {
$atts['href'] = ! empty( $item->url ) ? $item->url : '';
}
$atts = apply_filters( 'nav_menu_link_attributes', $atts, $item, $args );
$attributes = '';
foreach ( $atts as $attr => $value ) {
if ( ! empty( $value ) ) {
$value = ( 'href' === $attr ) ? esc_url( $value ) : esc_attr( $value );
$attributes .= ' ' . $attr . '="' . $value . '"';
}
}
$item_output = $args->before;
/*
* Glyphicons
* ===========
* Since the the menu item is NOT a Divider or Header we check the see
* if there is a value in the attr_title property. If the attr_title
* property is NOT null we apply it as the class name for the glyphicon.
*/
if ( ! empty( $item->attr_title ) )
$item_output .= '<a'. $attributes .'><span class="glyphicon ' . esc_attr( $item->attr_title ) . '"></span> ';
else
$item_output .= '<a'. $attributes .'>';
$item_output .= $args->link_before . apply_filters( 'the_title', $item->title, $item->ID ) . $args->link_after;
$item_output .= ( $args->has_children && 0 === $depth ) ? ' <span class="icon-play"></span></a>' : '</a>';
$item_output .= $args->after;
$output .= apply_filters( 'walker_nav_menu_start_el', $item_output, $item, $depth, $args );
}
}
/**
* Traverse elements to create list from elements.
*
* Display one element if the element doesn't have any children otherwise,
* display the element and its children. Will only traverse up to the max
* depth and no ignore elements under that depth.
*
* This method shouldn't be called directly, use the walk() method instead.
*
* #see Walker::start_el()
* #since 2.5.0
*
* #param object $element Data object
* #param array $children_elements List of elements to continue traversing.
* #param int $max_depth Max depth to traverse.
* #param int $depth Depth of current element.
* #param array $args
* #param string $output Passed by reference. Used to append additional content.
* #return null Null on failure with no changes to parameters.
*/
public function display_element( $element, &$children_elements, $max_depth, $depth, $args, &$output ) {
if ( ! $element )
return;
$id_field = $this->db_fields['id'];
// Display this element.
if ( is_object( $args[0] ) )
$args[0]->has_children = ! empty( $children_elements[ $element->$id_field ] );
parent::display_element( $element, $children_elements, $max_depth, $depth, $args, $output );
}
/**
* Menu Fallback
* =============
* If this function is assigned to the ods_nav_menu's fallback_cb variable
* and a manu has not been assigned to the theme location in the WordPress
* menu manager the function with display nothing to a non-logged in user,
* and will add a link to the WordPress menu manager if logged in as an admin.
*
* #param array $args passed from the ods_nav_menu function.
*
*/
public static function fallback( $args ) {
if ( current_user_can( 'manage_options' ) ) {
extract( $args );
$fb_output = null;
if ( $container ) {
$fb_output = '<' . $container;
if ( $container_id )
$fb_output .= ' id="' . $container_id . '"';
if ( $container_class )
$fb_output .= ' class="' . $container_class . '"';
$fb_output .= '>';
}
$fb_output .= '<ul';
if ( $menu_id )
$fb_output .= ' id="' . $menu_id . '"';
if ( $menu_class )
$fb_output .= ' class="' . $menu_class . '"';
$fb_output .= '>';
$fb_output .= '<li>Add a menu</li>';
$fb_output .= '</ul>';
if ( $container )
$fb_output .= '</' . $container . '>';
echo $fb_output;
}
}
}
And you can display menu with
<?php
wp_nav_menu( array(
'menu' => your_menu_name,
'theme_location' => your_menu_location,
'depth' => 3,
'container' => '',
'container_class' => '',
'container_id' => 'bs-example-navbar-collapse-1',
'menu_class' => 'navigation',
'fallback_cb' => 'ods_bootstrap_navwalker::fallback',
'walker' => new ods_bootstrap_navwalker())
);
?>
Take a reference to https://developer.wordpress.org/reference/classes/walker_nav_menu/
The easy code is here you can customize your HTML whatever you want
$parent = array();
$menu_name = 'menu-1'; //change menu name
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);
$parent_id = 0;
foreach((array)$menu_items as $key => $menu_item)
{
if($menu_item->menu_item_parent == 0)
{
$parent_id = $menu_item->db_id;
$title = $menu_item->title;
$url = $menu_item->url;
array_push($parent, array("title" => $title, "url" => $url, "child" => array()));
}
else if($menu_item->menu_item_parent == $parent_id)
{
$childId = $menu_item->ID;
$title = $menu_item->title;
$url = $menu_item->url;
array_push($parent[count($parent) - 1]["child"], array("ID"=>$childId,"title" => $title, "url" => $url));
}
}
}
foreach($parent as $key=>$childval){
$parent[$key]['child']['subchild'];
if (!empty($childval['child'])) {
for($i=0;$i<count($childval['child']);$i++){
foreach($menu_items as $childitems){
if($childitems->menu_item_parent==$childval['child'][$i]['ID']){
$values = array("ID"=>$childitems->ID,"title" => $childitems->title, "url" => $childitems->url);
$parent[$key]['child'][$i]['childvalue'][] = $values;
}
}
}
}
}
echo "<pre>";
print_r($parent);
?
this will print your array then modify according to you.
thank me later..
the demo HTML is here
<ul class="nav navbar-nav">
<?php
foreach ($parent as $key => $value)
{
if(empty($value["child"]))
{
echo "<li><a href='" . $value["url"] . "'>" . $value["title"] . "</a></li>";
}
else
{
echo '<li class="dropdown">' . $value["title"] . ' <b class="caret"></b><ul class="dropdown-menu">';
foreach ($value["child"] as $key => $value)
{
echo '<li>' . $value["title"] . '</li>';
echo "<ul>";
foreach ($value['childvalue'] as $childval ){
echo '<li>' . $childval["title"] . '</li>';
}
echo "</ul>";
}
echo '</ul></li>';
}
}
?>
</ul>
You can use this code to display 3 levels hierarchical menu
Add this code in the function.php file.
function create_bootstrap_menu( $theme_location ) {
if ( ($theme_location) && ($locations = get_nav_menu_locations()) && isset($locations[$theme_location]) ) {
$menu = get_term( $locations[$theme_location], 'nav_menu' );
$menu_items = wp_get_nav_menu_items($menu->term_id);
//echo '<pre>'; print_r($menu_items); die;
$menu_list = '<ul>';
$menucount = 1;
$bool = true;
foreach( $menu_items as $menu_item ) {
if( $menu_item->menu_item_parent == 0 ) {
$parent = $menu_item->ID;
$menu_array = array();
foreach( $menu_items as $submenu ) {
if( $submenu->menu_item_parent == $parent ) {
$bool = true;
$menu_array[] = '<li class="dropdown"><a href="' . $submenu->url . '" >' . $submenu->title . '</a><ul>';
$parents = $submenu->ID;
foreach($menu_items as $submenus){
if( $submenus->menu_item_parent == $parents ) {
$menu_array[] .= '<li><a href="' . $submenus->url . '" >' . $submenus->title . '</a></li>';
}
}
$menu_array[] .= '</ul></li>';
}
}
if( $bool == true && count( $menu_array ) > 0 ) {
$menu_list .= '<li class="dropdown">';
$menu_list .= '<span>'.$menu_item->title.'</span> <i class="bi bi-chevron-down"></i>';
$menu_list .= '<ul>' ."\n";
$menu_list .= implode( $menu_array );
$menu_list .= '</ul>';
} else {
// echo "<pre>"; print_r($menu_item);
$menu_list .= '<li>';
$menu_list .= '<a class="nav-link scrollto active" href="'.$menu_item->url.'">' . $menu_item->title . '</a>';
}
}
// end <li>
$menu_list .= '</li>';
$menucount++;
}
} else {
$menu_list = '<!-- no menu defined in location "'.$theme_location.'" -->';
}
return $menu_list;
}
Add this code to your menu page(header.php)
<?php echo create_bootstrap_menu('primary'); ?>
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 have found a WooCommerce code snippet that adds sorting on Category pages, and it works.
But the problem is that the sorting subcategory is only displayed on the Parent category page, but not in the subcategories pages.
This is the category page:
The problem is that the sorting is not working on subcategory pages:
What do I need to change in my code to achieve that?
Here is my code:
function tutsplus_product_subcategories( $args = array()) {
$parentid = get_queried_object_id();
$args = array(
'parent' => $parentid
);
$terms = get_terms( 'product_cat', $args );
if ( $terms) {
echo '<ul class="wooc_sclist">';
foreach ( $terms as $term ) {
echo '<li class="category">';
echo '<h2>';
echo '<a href="' . esc_url( get_term_link( $term ) ) . '" class="' . $term->slug . '">';
echo $term->name;
echo '</a>';
echo '</h2>';
echo '</li>';
}
echo '</ul>';
}
}
add_action( 'woocommerce_before_shop_loop', 'tutsplus_product_subcategories', 50 );
Reference: Display WooCommerce Categories, Subcategories, and Products in Separate Lists
Well i found out how to make it so i will share it with you:
function sub_fillter(){
$sub = wp_get_post_terms(get_the_ID(),'product_cat');
if ( is_subcategory()){
$cat = array_shift( $sub );
$cat=get_term_top_most_parent($cat->term_id,'product_cat');
tutsplus_product_subcategories($cat);
}
}
add_action('woocommerce_before_shop_loop', 'sub_fillter', 50);
function is_subcategory (){
$cat = get_query_var('cat');
$category = get_category($cat);
$category_gt_parent="";
return ( $category_gt_parent == '0' ) ? false : true;
}
function tutsplus_product_subcategories( $cat) {
$parentid = $cat->term_id;
$args = array(
'parent' => $parentid
);
$terms = get_terms( 'product_cat', $args );
if ( $terms || is_subcategory()) {
echo '<ul class="wooc_sclist">';
foreach ( $terms as $term ) {
echo '<li class="category">';
echo '<h2>';
echo '<a href="' . esc_url( get_term_link( $term ) ) . '" class="' . $term->slug . '">';
echo $term->name;
echo '</a>';
echo '</h2>';
echo '</li>';
}
echo '</ul>';
}
}
I've been trying to display the subtitle for each children page of the current parent page. At the moment i've got it working so that it shows all the title's of the children pages.
Basically I want it to show the subtitle underneath each title of the children pages.
// Get childern
$children = ($post->post_parent) ? wp_list_pages('title_li=&child_of='.$post->post_parent.'&echo=0') : wp_list_pages('title_li=&child_of='.$post->ID.'&echo=0');
// Set subtitle
$subtitle = get_the_title($post->the_subtitle);
echo $children;
echo $subtitle;
Any help will be much appreciated.
Cheers
You have two options for this:
1. The OOP approach
Extend the Walker_page class to create a custom iterator to use with wp_list_pages().
Add this to your functions.php:
class Subtitle_walker extends Walker_page {
function start_el(&$output, $page, $depth, $args, $current_page) {
if ( $depth )
$indent = str_repeat("\t", $depth);
else
$indent = '';
extract($args, EXTR_SKIP);
$css_class = array('page_item', 'page-item-'.$page->ID);
if ( !empty($current_page) ) {
$_current_page = get_page( $current_page );
_get_post_ancestors($_current_page);
if ( isset($_current_page->ancestors) && in_array($page->ID, (array) $_current_page->ancestors) )
$css_class[] = 'current_page_ancestor';
if ( $page->ID == $current_page )
$css_class[] = 'current_page_item';
elseif ( $_current_page && $page->ID == $_current_page->post_parent )
$css_class[] = 'current_page_parent';
} elseif ( $page->ID == get_option('page_for_posts') ) {
$css_class[] = 'current_page_parent';
}
$css_class = implode( ' ', apply_filters( 'page_css_class', $css_class, $page, $depth, $args, $current_page ) );
//Added subtitle support
$output .= $indent . '<li class="' . $css_class . '">' . $link_before . apply_filters( 'the_title', $page->post_title, $page->ID ) . $link_after . get_the_post_thumbnail($page->ID, array(72,72)) .''.' <span class="subtitle">'.get_the_subtitle($page->ID,'','',0).'</span>';
if ( !empty($show_date) ) {
if ( 'modified' == $show_date )
$time = $page->post_modified;
else
$time = $page->post_date;
$output .= " " . mysql2date($date_format, $time);
}
}
}
And call it in your template file:
$subtitle_menu = new Subtitle_walker();
$args = array(
'title_li' => '',
'child_of' => ($post->post_parent) ? $post->post_parent : $post->ID,
'echo' => 0,
'walker' => $subtitle_menu
);
$children = wp_list_pages($args);
echo $children;
Note: the previous code is based on this tutorial, you can read it to get an in-depth explanation of what it's doing
2. Using custom WP_Query loop
You can use get_page_children to query the child pages and then loop through them and build/echo your custom list items:
//Set up the objects needed
$my_wp_query = new WP_Query();
$all_wp_pages = $my_wp_query->query(array('post_type' => 'page'));
//Get children
$children = ($post->post_parent) ? get_page_children( $post->post_parent, $all_wp_pages ) : get_page_children( $post->ID, $all_wp_pages );
//Build custom items
foreach($children as $child){
echo '<li class="page-item">';
echo ''.get_the_title($child->ID).'<br/>';
echo '<span class="subtitle">'.get_the_subtitle($child->ID).'</span>';
echo '</li>';
}