Wordpress - Showing custom menu links while showing child pages - php

I have this loop that simply shows all child pages of the current page:
<?php
$args = array(
'parent' => $post->ID,
'post_type' => 'page',
'sort_order' => 'ASC'
);
$pages = get_pages($args); ?>
<?php foreach( $pages as $page ) { ?>
<div>
<p><?php echo $page->post_title; ?></p>
</div>
<?php } ?>
The Nav for this page looks like this:
Parent Page
- Child page
- Child page
- Child page
- Custom Link (added in appearance > menus)
- Custom link (added in appearance > menus)
- Page which has another parent (added in appearance > menus)
The code above correctly shows all of the direct child pages, but I would like it to show the custom links and other page I have added to the menu dropdown.
Ive tried playing with wp_get_nav_menu_items in place of get_pages and also using 'post_type' => 'page' but I can't seem to get this working correctly. I can either show a full list of all pages or just the direct child pages.
Can anyone tell me where I'm going wrong please? I seems like it should be a really easy thing to do...

Ok, I've found a way to get this to work using a custom walker class inside the functions.php file, like so:
class Selective_Walker extends Walker_Nav_Menu
{
function walk( $elements, $max_depth) {
$args = array_slice(func_get_args(), 2);
$output = '';
if ($max_depth < -1) //invalid parameter
return $output;
if (empty($elements)) //nothing to walk
return $output;
$id_field = $this->db_fields['id'];
$parent_field = $this->db_fields['parent'];
// flat display
if ( -1 == $max_depth ) {
$empty_array = array();
foreach ( $elements as $e )
$this->display_element( $e, $empty_array, 1, 0, $args, $output );
return $output;
}
/*
* need to display in hierarchical order
* separate elements into two buckets: top level and children elements
* children_elements is two dimensional array, eg.
* children_elements[10][] contains all sub-elements whose parent is 10.
*/
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( 0 == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
/*
* when none of the elements is top level
* assume the first one must be root of the sub elements
*/
if ( empty($top_level_elements) ) {
$first = array_slice( $elements, 0, 1 );
$root = $first[0];
$top_level_elements = array();
$children_elements = array();
foreach ( $elements as $e) {
if ( $root->$parent_field == $e->$parent_field )
$top_level_elements[] = $e;
else
$children_elements[ $e->$parent_field ][] = $e;
}
}
$current_element_markers = array( 'current-menu-item', 'current-menu-parent', 'current-menu-ancestor' ); //added by continent7
foreach ( $top_level_elements as $e ){ //changed by continent7
// descend only on current tree
$descend_test = array_intersect( $current_element_markers, $e->classes );
if ( !empty( $descend_test ) )
$this->display_element( $e, $children_elements, 2, 0, $args, $output );
}
/*
* if we are displaying all levels, and remaining children_elements is not empty,
* then we got orphans, which should be displayed regardless
*/
/* removed by continent7
if ( ( $max_depth == 0 ) && count( $children_elements ) > 0 ) {
$empty_array = array();
foreach ( $children_elements as $orphans )
foreach( $orphans as $op )
$this->display_element( $op, $empty_array, 1, 0, $args, $output );
}
*/
return $output;
}
}
Then using this to my template page to simply the menu:
<?php
$menuParameters = array(
'theme_location' =>'primary',
'walker'=>new Selective_Walker()
);
echo wp_nav_menu( $menuParameters );
?>
This helpfully shows the current page title as well as ALL of the sub nav items, not just the page items.

Related

WordPress/PHP get list of all links in primary navigation

I am trying to create a list of all links in primary navigation on my WordPress site. I am trying to output the link as URL: in a script tag using a foreach loop.
I am stuck trying to list of of the navitems links using $navItem->url in my foreach loop. It doesn't return anything / false.
Instead of $locations[$menuLocations] I have tried simply just write primary menu name as well: $locations['primary'] but no luck so far.
function get_nav_menu_items_by_location( $location, $args = [] ) {
$html = '<script type="application/ld+json" alt="hejtest">';
// Get all locations
$menuLocations = get_nav_menu_locations();
// Get object id by location
$object = wp_get_nav_menu_object( $locations[$menuLocations] );
// Get menu items by menu name
$menu_items = wp_get_nav_menu_items($object);
foreach ( $menu_items as $navItem ) {
$html .= '"url":"' . $navItem->url . '"';
}
$html .= "</script>";
echo $html;
}
add_action('wp_head', 'get_nav_menu_items_by_location');
Well... I did a refactor of your code and it's works ;)
Try with this way:
function get_nav_items() {
$menu_slug_to_retrieve = 'main'; // This can be main, primary, what ever...
$locations = get_nav_menu_locations();
$menu = wp_get_nav_menu_object( $locations[ $menu_slug_to_retrieve ] );
$menu_items = wp_get_nav_menu_items( $menu->term_id );
$menu_items_json = array(); // Prepare the array to convert to json
// Loop it
if ( $menu_items ) {
foreach ( $menu_items as $item ) {
$menu_items_json[] = array( 'url' => $item->url );
}
$html = sprintf(
'<script type="application/ld+json" id="custom-json">%s</script>',
json_encode( $menu_items_json )
);
echo $html;
}
}
add_action( 'wp_head', 'get_nav_items' );
Regards!

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;
}

How to get sub items of parent menu item?

I have created a menu which have several items (WooCommerce categories), each of them having few child items (WooCommerce products).
I'm struggling to retrieve the sub items of a parent menu item.
Im getting the parent item using this code:
$the_menu = wp_get_nav_menu_object('Some Menu');
$the_menu_items = wp_get_nav_menu_items($the_menu);
foreach ($the_menu_items as $index => $menu_item) {
if ($menu_item->object_id == $category->term_id ) {
$category_submenu = $menu_item;
}
}
How can I retrieve the child items of current parent item?
Thank you in advance!
You need a recursion for all childs.
Here the example
if ( ! function_exists( 'recursive_mitems_to_array' ) ) {
/**
* #param $items
* #param int $parent
*
* #return array
*/
function recursive_mitems_to_array( $items, $parent = 0 )
{
$bundle = [];
foreach ( $items as $item ) {
if ( $item->menu_item_parent == $parent ) {
$child = recursive_mitems_to_array( $items, $item->ID );
$bundle[ $item->ID ] = [
'item' => $item,
'childs' => $child
];
}
}
return $bundle;
}
}
Usage:
$items = wp_get_nav_menu_items( "Default Theme Menu" ); // Your menu title
$build_tree = recursive_mitems_to_array( $items );
var_dump(build_tree);
Note: correct for your needs.
maybe you need to output html with values.
This example should return nested array (not fully tested)
Here is my solution. Looping through menu "MENU NAME" and then showing only items that have the parent PARENT_ID.
<ul>
<? foreach (wp_get_nav_menu_items('MENU_NAME') as $r) {
if ($r->menu_item_parent == PARENT_ID) { echo '<li>'.$r->title.'</li>'; }
} ?>
</ul>
As a workaround I have this temporary solution:
$the_menu = wp_get_nav_menu_object('Some Menu');
$the_menu_items = wp_get_nav_menu_items($the_menu);
$category_products = [];
foreach ($the_menu_items as $index => $menu_category) {
if ($menu_category->object_id == $category->term_id ) {
$category_submenu = $menu_category;
}
}
foreach ($the_menu_items as $index => $menu_product) {
if ($menu_product->type_label == 'Product'
&& $menu_product->menu_item_parent == $category_submenu->ID
) {
$category_products[] = $menu_product;
}
}
I'm parsing all menu items (both parent items and sub items), getting the necessary item based on current category ID and then retrieving the sub items (products) of each category by parsing all the items one more time and checking if current item (product) parent ID matches with the parent category item ID and I just put it into an array.

List categories order by Random

I'm not good with PHP.
I use "Advanced Categories Widget" to list categories on Sidebar.
I used this plugin because it offers the ability to display images categories.
But I need to order categories by random.
I find this code on the plugin:
function advanced_categories_widget_html( $args = array() ) {
$args = wp_parse_args( $args );
$args['walker'] = new Walker_Advance_Category_Widget;
$output = wp_list_categories( $args );
if ( $output ) return $output;
}
and i Find another code on forum which displays correctly categories by random order:
wp_list_categories
how I can exploit the second code to hack the first code to list my categories with random order?
The PHP file for the plugin: http://codepad.org/a3yU7Xny
Accordion to the documentation on the Advanced Categories Widget plugin you're using -- -- you can specify 'orderby' in your plugin settings. See this screenshot.
You should have a random or rand option in the drop down.
Can't confirm cuz it's a paid plugin.
Just add the "hack" the function in the plugin file :
function advanced_categories_widget_html( $args = array() ) {
$args = wp_parse_args( $args );
$args['walker'] = new Walker_Advance_Category_Widget;
$cats ='';
$categories=get_categories();
$rand_keys = array_rand($categories, 5); // 5 is the number of categories you want
foreach ($rand_keys as $key) {
$cats .= $categories[$key]->term_id .',';
}
$output = wp_list_categories($args.'&include='.$cats);
if ( $output ) return $output;
}
Or by a cleaner way, add in your functions.php file :
function random_advanced_categories_widget_html( $args = array() ) {
$args = wp_parse_args( $args );
$args['walker'] = new Walker_Advance_Category_Widget;
$cats ='';
$categories=get_categories();
$rand_keys = array_rand($categories, 5); // 5 is the number of categories you want
foreach ($rand_keys as $key) {
$cats .= $categories[$key]->term_id .',';
}
$output = wp_list_categories($args.'&include='.$cats);
if ( $output ) return $output;
}

wordpress wp_nav_menu hide empty categories

im am searching for a way to hide empty categories from a custom menu in wordpress?
I have build a massive hierarchy and now i am going to fill the categories one after one. But until I have put a post in it, I want to hide it from the menu.
Seems that I need something similar like hide_empty=1 for "wp_list_categories" but now for "wp_nav_menu".
Add the following code to child theme's functions.php file:
/*
* Hide empty categories from nav menus
*/
add_filter( 'wp_get_nav_menu_items', 'gowp_nav_remove_empty_terms', 10, 3 );
function gowp_nav_remove_empty_terms ( $items, $menu, $args ) {
global $wpdb;
$empty = $wpdb->get_col( "SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE count = 0" );
foreach ( $items as $key => $item ) {
if ( ( 'taxonomy' == $item->type ) && ( in_array( $item->object_id, $empty ) ) ) {
unset( $items[$key] );
}
}
return $items;
}
We can't compare "wp_list_categories" with "wp_nav_menu". We don't have that hide_empty option for "wp_nav_menu". Only solution is we have to write our own menu or else we have to use hooks for "wp_nav_menu".
The accepted answer is effective, but it destroyed the menu structure for me, meaning the ul > li > ul.sub-menu > li > and so on. Rather than unset the item, I added a new class to the item->classes array to hide it in css.
if (!is_admin()) add_filter('wp_get_nav_menu_items', 'wp_nav_remove_empty_terms', 10, 3);
function wp_nav_remove_empty_terms ($items, $menu, $args) {
global $wpdb;
$empty = $wpdb->get_col("SELECT term_taxonomy_id FROM $wpdb->term_taxonomy WHERE count = 0");
foreach ($items as $key => $item) {
if (('taxonomy' == $item->type )
&& (in_array( $item->object_id, $empty))) {
//unset( $items[$key] );
$item->classes[] = 'hide';
}
}
return $items;
}

Categories