Current level navigation in Wordpress - php

I have a 3 level navigation:
Home
> submenu1
>> sub3
> submenu
>> sub4
>> sub5 // current page
About
> about2
>> sub6
> about3
>> sub7
I am trying to get each navigation level separately,
With the example nav above, being on the sub5 page, I would need
Current 1st level nav: Home
Current parallel 2nd level nav: submenu1, submenu (who are both under Home)
Current parallel 3rd level nav: sub4, sub5 (who are menu under submenu)
I will need to modify the elements and styles of the menu and will need to work with menu items in php, such as:
$menu_name = 'topnav';
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;
[...]
Please help getting the navigation layers separately to show up as explained above.
I got the 1st and 3rd level working, but can't get the current parallel 2nd level nav to show up correctly.
Thanks.
Current Code: 2nd nav works:
<?php
$menu_name = 'topnav';
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 );
// Convert Objects to Arrays, Enables us to use Array Filter
$json = json_encode($menu_items);
$menu_items = json_decode($json, true);
// Current Page
$child = get_the_id();
$current_level = array_filter( $menu_items, function($v, $k) use ($child) {
return $v['object_id'] == $child;
}, ARRAY_FILTER_USE_BOTH );
$current_level_keys = array_keys($current_level);
$parent = $current_level[$current_level_keys[0]]['menu_item_parent'];
if( !empty( $parent ) )
{
$current_level_items = array_filter( $menu_items, function($v, $k) use ($parent) {
return $v['menu_item_parent'] == $parent;
}, ARRAY_FILTER_USE_BOTH );
} else {
$current_level_items = $current_level[$current_level_keys[0]];
}
//echo '1:';
//echo '<pre>';
//print_r($current_level_items);
//echo '</pre>';
//foreach ($current_level_items as $k => $v) {
// echo '<li>'.$v['title'].'</li>';
//}
$parent_level = array_filter( $menu_items, function($v, $k) use ($parent) {
return $v['ID'] == $parent;
}, ARRAY_FILTER_USE_BOTH );
$parent_level_keys = array_keys($parent_level);
$grand_parent = $parent_level[$parent_level_keys[0]]['menu_item_parent'];
if( !empty( $grand_parent ) )
{
$parent_level_items = array_filter( $menu_items, function($v, $k) use ($grand_parent) {
return $v['menu_item_parent'] == $grand_parent;
}, ARRAY_FILTER_USE_BOTH );
} else {
$parent_level_items = $parent_level[$parent_level_keys[0]];
}
//echo '2:';
//echo '<pre>';
//print_r($parent_level_items);
//echo '</pre>';
//foreach ($parent_level_items as $k => $v) {
//echo '<li>'.$v['title'].'</li>';
//}
$grand_parent_level = array_filter( $menu_items, function($v, $k) use ($grand_parent) {
return $v['ID'] == $grand_parent;
}, ARRAY_FILTER_USE_BOTH );
$grand_parent_level_keys = array_keys($grand_parent_level);
$great_grand_parent = $grand_parent_level[$grand_parent_level_keys[0]];
if( !empty( $parent ) ) {
if( !empty( $great_grand_parent ) ) {
echo '<li class="custom-page-title">'.$great_grand_parent['title'].'</li>';
if (is_array($parent_level_items)) {
foreach ($parent_level_items as $k => $v) {
echo '<li>'.$v['title'].'</li>';
}
}
} else {
echo '<li class="custom-page-title">'.$parent_level_items['title'].'</li>';
if (is_array($current_level_items)) {
foreach ($current_level_items as $k => $v) {
echo '<li>'.$v['title'].'</li>';
}
}
}
}
//echo '3:';
//echo '<pre>';
//print_r($great_grand_parent);
//echo '</pre>';
}
?>
However if there are duplicate (3rd level) pages in the menu, this script only takes the first parent... Could it be the last or the real parent (from url path maybe?)
Duplicate page in menu issue:
Home
> submenu1
>> sub3
> submenu
>> sub4
>> sub5 // current page
About
> about2
>> sub6
> about3
>> sub7
>> sub5 // duplicate page
When visiting the duplicate page (sub5), the parent returned is the first one (Home > submenu), when it should be (About > about3)
Please help getting this fixed...

Edit:
To address the duplicate situation you have to add a line of code. You can add one of the below lines based on the parent chain you want to select. I am also adding these lines in the actual code to show you where they go.
// Get First Parent Chain
$current_level = array_values(array_slice($current_level, 0, 1));
// Get Last Parent Chain
$current_level = array_values(array_slice($current_level, -1, 1));
Original Answer: ( Also updated with the above lines )
This code will give you all levels items, it is written for only three levels as per your requirements but you can use the logic and reiterate the code to as many levels as needed, or better yet write up something recursive.
$menu_name = 'topnav';
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 );
// Convert Objects to Arrays, Enables us to use Array Filter
$json = json_encode($menu_items);
$menu_items = json_decode($json, true);
// Current Page
$child = get_the_id();
$current_level = array_filter( $menu_items, function($v, $k) use ($child) {
return $v['object_id'] == $child;
}, ARRAY_FILTER_USE_BOTH );
// Get First Parent Chain ( Uncomment below line if you want to use this )
//$current_level = array_values(array_slice($current_level, 0, 1));
// Get Last Parent Chain ( Uncomment below line if you want to use this )
//$current_level = array_values(array_slice($current_level, -1, 1));
$current_level_keys = array_keys($current_level);
$parent = $current_level[$current_level_keys[0]]['menu_item_parent'];
if( !empty( $parent ) )
{
$current_level_items = array_filter( $menu_items, function($v, $k) use ($parent) {
return $v['menu_item_parent'] == $parent;
}, ARRAY_FILTER_USE_BOTH );
} else {
$current_level_items = $current_level[$current_level_keys[0]];
}
echo '<pre>';
print_r($current_level_items);
echo '</pre>';
$parent_level = array_filter( $menu_items, function($v, $k) use ($parent) {
return $v['ID'] == $parent;
}, ARRAY_FILTER_USE_BOTH );
$parent_level_keys = array_keys($parent_level);
$grand_parent = $parent_level[$parent_level_keys[0]]['menu_item_parent'];
if( !empty( $grand_parent ) )
{
$parent_level_items = array_filter( $menu_items, function($v, $k) use ($grand_parent) {
return $v['menu_item_parent'] == $grand_parent;
}, ARRAY_FILTER_USE_BOTH );
} else {
$parent_level_items = $parent_level[$parent_level_keys[0]];
}
echo '<pre>';
print_r($parent_level_items);
echo '</pre>';
$grand_parent_level = array_filter( $menu_items, function($v, $k) use ($grand_parent) {
return $v['ID'] == $grand_parent;
}, ARRAY_FILTER_USE_BOTH );
$grand_parent_level_keys = array_keys($grand_parent_level);
$great_grand_parent = $grand_parent_level[$grand_parent_level_keys[0]];
echo '<pre>';
print_r($great_grand_parent);
echo '</pre>';
}
I have tried to write it as self explanatory, but if you have any questions i will be happy to help.

Related

PHP | Add elements to the array and sort after loop

I'm trying to get some numeric values ​​out, recovered thanks to: wp_get_post_terms, which retrieves me the daughter taxonomies of customized taxonomies, where prices are inserted in them in numerical form.
The goal is to display prices using sort and rsort, in the order decided.
When recovering the values, however, I am wrong to add elements to the array and sort them later
code:
foreach ($qprice as $r)
{
$children = $r->term_id;
$child_terms = get_term_children($r->term_id, 'servizi_pro');
$all_terms = wp_get_post_terms($r->ID, 'servizi_pro');
foreach ( $all_terms as $term )
{
if (!in_array($term->term_id, $child_terms))
continue;
$price = $term->name;
$final_price = [$price];
}
rsort($final_price, SORT_NUMERIC);
foreach($final_price as $pr_f) {
echo '<span class="numberCircle"><span>'.str_pad($pr_f, 9).''.'€'.'</span></span>';
}
}
plus:
$final_price = [];
foreach ($qprice as $r)
{
$children = $r->term_id;
$child_terms = get_term_children($r->term_id, 'servizi_pro');
$all_terms = wp_get_post_terms($r->ID, 'servizi_pro');
foreach ( $all_terms as $term )
{
if (!in_array($term->term_id, $child_terms))
continue;
$price = $term->name;
$final_price[] = $price;
}
}
sort($final_price, SORT_NUMERIC);
foreach($final_price as $pr_f) {
echo '<span class="numberCircle"><span>'.str_pad($pr_f, 9).'€'.'</span></span>';
}
You need to initialize the $final_price array before the inner foreach loop, and push onto the array rather than replacing it each time through the loop.
foreach ($qprice as $r)
{
$children = $r->term_id;
$child_terms = get_term_children($r->term_id, 'servizi_pro');
$all_terms = wp_get_post_terms($r->ID, 'servizi_pro');
$final_price = [];
foreach ( $all_terms as $term )
{
if (!in_array($term->term_id, $child_terms))
continue;
$price = $term->name;
$final_price[] = $price;
}
rsort($final_price, SORT_NUMERIC);
foreach($final_price as $pr_f) {
echo '<span class="numberCircle"><span>'.str_pad($pr_f, 9).''.'€'.'</span></span>';
}
}

Trying to add a custom item to a specific WordPress menu item position but it fails

I am trying to insert a menu item inside a wordpressmenu and found this example that not works for me. The Industries menu is a single-menu
add_filter('wp_nav_menu_items','add_custom_in_menu', 10, 2);
function add_custom_in_menu( $items, $args )
{
if( $args->theme_location == 'Industries' )
{
$items_array = array();
while ( false !== ( $item_pos = strpos ( $items, '<li', 5 ) ) )
{
$items_array[] = substr($items, 0, $item_pos);
$items = substr($items, $item_pos);
}
$items_array[] = $items;
array_splice($items_array, 4, 0, '<li>custom HTML here</li>');
$items = implode('', $items_array);
}
return $items;
}

howto iterate menu items in wordpress?

I read wordpress code documents about how to iterate menu items, and then I wrote this code in index.php:
<?php
$menu_name = 'top_menu';
$array_menu = wp_get_nav_menu_items($menu_name);
foreach ((array)$array_menu as $mol) ;
{
echo $mol;
}
?>
But it doesn't work. And is returning nothing. Casting to array didn't help so.
I need to echo items menu title one by one. without any html list tags.
$navItem is an object, so you can not just echo it, try to echo its properties like this:
foreach ($array_menu as $navItem ) {
echo '<li>'.$navItem->title.'</li>';
}
This might help:
<?php
function get_menu_items_by_registered_slug($menu_slug) {
$menu_items = array();
if ( ( $locations = get_nav_menu_locations() ) && isset( $locations[ $menu_slug ] ) ) {
$menu = get_term( $locations[ $menu_slug ] );
$menu_items = wp_get_nav_menu_items($menu->term_id);
}
return $menu_items;
}
$show_menus = [];
$menus = get_menu_items_by_registered_slug('primary');
foreach( $menus as $menu ) {
$show_menus[] = $menu->title;
}
echo '<pre>';
print_r($show_menus);
?>

How to get current page menu item ancestor name wordpress

I would like to know how to retrieve as variable the name of the current page.
For exemple, My menu is like :
Item 1
Item 2 => sub-item 1
Item 1 & 2 are custom links. sub-item1 is my page.
When I'm on this page, I want to retrieve "Item 2" name (to build my custom breadcumbs)
Thanks
BREADCRUMBS:
Here is a breadcrumbs function that work with the hierarchy of your menu. Add this to your theme's 'functions.php'.
function my_menu_breadcrumb($theme_location, $separator = ' > ') {
$locations = get_nav_menu_locations();
if ( isset( $locations[ $theme_location ] ) ) {
$menu = wp_get_nav_menu_object( $locations[ $theme_location ] );
$menu_items = wp_get_nav_menu_items($menu->term_id);
_wp_menu_item_classes_by_context( $menu_items );
$breadcrumbs = array();
foreach ( $menu_items as $menu_item ) {
if ($menu_item->current) {
$breadcrumbs[] = "<span title=\"{$menu_item->title}\">{$menu_item->title}</span>";
}
else if ($menu_item->current_item_ancestor) {
$breadcrumbs[] = "{$menu_item->title}";
}
}
echo implode($separator, $breadcrumbs);
}
}
You can then call as <?php my_menu_breadcrumb('header-menu'); ?> where 'header-menu' is the menu location name. In the loop, $menu_item->title will return page title and $menu_item->url it's URL.
PARENT MENU TITLE:
Here is the function to get parent menu item title(s) of current page - add this to your theme's 'functions.php'.
function my_menu_parent($theme_location) {
$locations = get_nav_menu_locations();
if ( isset( $locations[ $theme_location ] ) ) {
$menu = wp_get_nav_menu_object( $locations[ $theme_location ] );
$menu_items = wp_get_nav_menu_items($menu->term_id);
_wp_menu_item_classes_by_context( $menu_items );
$breadcrumbs = array();
foreach ( $menu_items as $menu_item ) {
if ($menu_item->current_item_ancestor) {
$breadcrumbs[] = $menu_item->title;
}
}
return $breadcrumbs;
}
}
You can then call as below where 'header-menu' is the menu location name.
$parentitems = my_menu_parent( 'header-menu' );
foreach ( $parentitems as $parentitem ) {
echo $parentitem."<br>";
}
You can user this
$pagename = get_query_var('pagename');
if ( !$pagename && $id > 0 ) {
// If a static page is set as the front page, $pagename will not be set. Retrieve it from the queried object
$post = $wp_query->get_queried_object();
$pagename = $post->post_name;
}
There are plenty of tutorials and code snippets online describing how to show the active page’s children (or if on one of those children, the active page’s siblings) as a sub-menu in WordPress. Usually something like this:
<?php
$ref_post = empty($post->post_parent) ? $post->ID : $post->post_parent;
$children = wp_list_pages('title_li=&child_of='.$ref_post.'&echo=0');
if ($children) {
echo "<ul>$children</ul>";
}
?>
for ref:-https://www.minddevelopmentanddesign.com/blog/showing-current-pages-parents-sub-menu-items-custom-nav-menu-wordpress/

Woocommerce breadcrumbs multiple categories

I have e commerce website in wordpress. There are lot of product in it & many product comes under multiple categories like 600 mah Power bank is comes under automobile, IT, media etc. My problem is when i go to the detail of a product there it by default pick up only one category no matter if go through IT category at the end it shows me automobile like this Home / Shop / industry / Automobile / 600 mah Power Bank. But i went to this product via IT so it should show me like this Home / Shop / industry / IT / 600 mah Power Bank.
How can iget path where i come from previous page?
if you are using Woocommerce you can use the following directly, if not it will need adapting but you get the idea:
if (get_post_type() == 'product' && is_single() && ! is_attachment()) {
echo $prepend;
if ($terms = get_the_terms($post->ID, 'product_cat')) {
$referer = wp_get_referer();
foreach ($terms as $term) {
$referer_slug = (strpos($referer, $term->slug));
if ($referer_slug == true) {
$category_name = $term->name;
$ancestors = get_ancestors($term->term_id, 'product_cat');
$ancestors = array_reverse($ancestors);
foreach ($ancestors as $ancestor) {
$ancestor = get_term($ancestor, 'product_cat');
if (! is_wp_error($ancestor) && $ancestor) {
echo $before . '' . $ancestor->name . '' . $after . $delimiter;
}
}
echo $before . '' . $category_name . '' . $after . $delimiter;
}
}
}
echo $before . get_the_title() . $after;
}
The main bulk of the work here is done by wp_get_referer which gets the referring URL of the product your visitor has navigated to. The rest of the code checks if a valid category is contained within the URL and uses it in the breadcrumb.
See Jonathon Js post here for more information http://www.cryoutcreations.eu/forums/t/wrong-breadcrumbs-displayed
Updated answer for anyone still encountering this issue on a newer version of WooCommerce, this solution worked for me on WooCommerce 3.5.4.
This code should be placed inside the following file path (i.e. a child theme)
/wp-content/themes/your-child-theme/woocommerce/global/breadcrumb.php
It will override the default WooCommerce breadcrumb code.
http://pastebin.com/raw/bemM8ZNF
/**
* Shop breadcrumb
*
* This template can be overridden by copying it to yourtheme/woocommerce/global/breadcrumb.php.
*
* HOWEVER, on occasion WooCommerce will need to update template files and you
* (the theme developer) will need to copy the new files to your theme to
* maintain compatibility. We try to do this as little as possible, but it does
* happen. When this occurs the version of the template file will be bumped and
* the readme will list any important changes.
*
* #see https://docs.woocommerce.com/document/template-structure/
* #author WooThemes
* #package WooCommerce/Templates
* #version 2.3.0
* #see woocommerce_breadcrumb()
if ( ! defined( 'ABSPATH' ) ) {
exit;
}
if ( $breadcrumb ) {
echo $wrap_before;
if ( is_single() && get_post_type() == 'product' ) {
echo $prepend;
if ( $terms = get_the_terms( $post->ID, 'product_cat' ) ) {
$referer = wp_get_referer();
$printed = array();
foreach( $terms as $term){
if(in_array($term->id, $printed)) continue;
$referer_slug = (strpos($referer, '/'.$term->slug.'/'));
if(!$referer_slug==false){
$printed[] = $term->id;
$category_name = $term->name;
$ancestors = get_ancestors( $term->term_id, 'product_cat' );
$ancestors = array_reverse( $ancestors );
foreach ( $ancestors as $ancestor ) {
$ancestor = get_term( $ancestor, 'product_cat' );
if ( ! is_wp_error( $ancestor ) && $ancestor )
echo $before . '' . $ancestor->name . '' . $after . $delimiter;
}
echo $before . '' . $category_name . '' . $after . $delimiter;
}
}
}
echo $before . get_the_title() . $after;
} else {
foreach ( $breadcrumb as $key => $crumb ) {
echo $before;
if ( ! empty( $crumb[1] ) && sizeof( $breadcrumb ) !== $key + 1 ) {
echo '' . esc_html( $crumb[0] ) . '';
} else {
echo esc_html( $crumb[0] );
}
echo $after;
if ( sizeof( $breadcrumb ) !== $key + 1 ) {
echo $delimiter;
}
}
}
echo $wrap_after;
}
C/O: Joris Witteman
I reworked the code a little bit to account for a primary category and a referred one, having infinite parents.
I used the breadcrumb filter as it is quite clean, directly in the single product template, but in case you need it elsewhere you can use the single conditional.
function breadcumbs_referred_or_primary ($main, $terms)
{
// Our primary term is 520 (hardcoded)
$referer = wp_get_referer();
$referredTerm = -1;
$referredTermIndex = -1;
$primaryTermId = 520; // hardcoded
$primaryTermIndex = -1;
foreach($terms as $key => $term) {
if ($referredTerm != -1) break; // we found it in a previous iteration!
$ancestors = get_ancestors( $term->term_id, 'product_cat');
array_unshift($ancestors, $term->term_id);
if ($primaryTermIndex == -1 && in_array($primaryTermId, $ancestors)) $primaryTermIndex = $key;
foreach ($ancestors as $ancestor) {
if($referredTerm != -1) break 2; // we found it in a previous iteration!
$ancestor = get_term( $ancestor, 'product_cat' );
$referer_slug = (strpos($referer, '/'.$ancestor->slug.'/'));
if (!$referer_slug==false) { // it's found in the current level
$referredTerm = $term->term_id;
$referredTermIndex = $key;
}
}
}
// we return either the browsed terms or the primary term
if ($referredTermIndex != -1) {
return $terms[$referredTermIndex];
} else {
return $terms[$primaryTermIndex];
}
}
add_filter('woocommerce_breadcrumb_main_term', 'breadcumbs_referred_or_primary', 10, 2);
My solution is:
if (!empty($breadcrumb)) {
echo $wrap_before;
if (is_single() && get_post_type() == 'product') {
$breadcrumb_diff = [];
$breadcrumb_diff[] = $breadcrumb[0];
if ($terms = get_the_terms($post->ID, 'product_cat')) {
$referer = wp_get_referer();
$site_url = site_url();
$referer = str_replace($site_url . '/zoomagazin/', '', $referer);
$referer_array = explode('/', $referer);
foreach ($referer_array as $term_slug) {
$get_term_by_slug = get_term_by('slug', $term_slug, 'product_cat');
$breadcrumb_diff[] = [$get_term_by_slug->name, get_term_link($term_slug, 'product_cat')];
}
$breadcrumb_diff[]= $breadcrumb[count($breadcrumb) - 1];
foreach ($breadcrumb_diff as $key => $crumb) {
echo $before;
if (!empty($crumb[1]) && sizeof($breadcrumb_diff) !== $key + 1) {
echo '' . esc_html($crumb[0]) . '';
} else {
echo esc_html($crumb[0]);
}
echo $after;
if (sizeof($breadcrumb) !== $key + 1) {
echo $delimiter;
}
}
}
} else {
foreach ($breadcrumb as $key => $crumb) {
echo $before;
if (!empty($crumb[1]) && sizeof($breadcrumb) !== $key + 1) {
echo '' . esc_html($crumb[0]) . '';
} else {
echo esc_html($crumb[0]);
}
echo $after;
if (sizeof($breadcrumb) !== $key + 1) {
echo $delimiter;
}
}
}
echo $wrap_after;
}

Categories