admin管理员组文章数量:1291454
I am conditionally showing menu items. All is fine but want to remove parent item if returning no child items.
For example if I have a parent menu (it will always be a custom link with #) called More and it has multiple child items (mostly one level). Now if I am hiding child items based on user roles and for some roles no child items available, in that case, I want to remove More menu items also since it has no child.
I am trying with the following code but since $item
is an object, array_search
won't work. So how can I check for child-parent and remove the menu item if it has no child?
array_search( $item[ 'ID' ], array_column( $item, 'menu_item_parent' ) )
Below is the working code that hides menu item based on user roles.
public static function exclude_menu_items( $items, $menu, $args ) {
if ( current_user_can( 'administrator' ) ) {
return $items;
}
foreach ( $items as $key => $item ) {
if ( $page = get_post( $item->object_id ) ) {
if ( $page->post_type == 'page' ) {
$template = get_post_meta( $page->ID, '_wp_page_template', TRUE );
$post_type = self::get_cpt_for_template( $template );
if ( $post_type && ( ! current_user_can( 'cp_access_' . $post_type ) || ! ( new self() )->is_current_user_granted_for_module( $post_type ) ) ) {
unset( $items[ $key ] );
}
}
}
}
return $items;
}
I am conditionally showing menu items. All is fine but want to remove parent item if returning no child items.
For example if I have a parent menu (it will always be a custom link with #) called More and it has multiple child items (mostly one level). Now if I am hiding child items based on user roles and for some roles no child items available, in that case, I want to remove More menu items also since it has no child.
I am trying with the following code but since $item
is an object, array_search
won't work. So how can I check for child-parent and remove the menu item if it has no child?
array_search( $item[ 'ID' ], array_column( $item, 'menu_item_parent' ) )
Below is the working code that hides menu item based on user roles.
public static function exclude_menu_items( $items, $menu, $args ) {
if ( current_user_can( 'administrator' ) ) {
return $items;
}
foreach ( $items as $key => $item ) {
if ( $page = get_post( $item->object_id ) ) {
if ( $page->post_type == 'page' ) {
$template = get_post_meta( $page->ID, '_wp_page_template', TRUE );
$post_type = self::get_cpt_for_template( $template );
if ( $post_type && ( ! current_user_can( 'cp_access_' . $post_type ) || ! ( new self() )->is_current_user_granted_for_module( $post_type ) ) ) {
unset( $items[ $key ] );
}
}
}
}
return $items;
}
Share
Improve this question
edited Jun 1, 2020 at 16:07
fuxia♦
107k38 gold badges255 silver badges459 bronze badges
asked Jun 1, 2020 at 7:16
pixelngrainpixelngrain
1,3901 gold badge23 silver badges50 bronze badges
2 Answers
Reset to default 1If you want to use the wp_get_nav_menu_items
hook, then try the function below which should be hooked after the exclude_menu_items
function:
// Example when NOT using a class: (you already defined the exclude_menu_items function)
add_filter( 'wp_get_nav_menu_items', 'exclude_menu_items', 10, 3 );
add_filter( 'wp_get_nav_menu_items', 'exclude_menu_items2' );
function exclude_menu_items2( $items ) {
if ( current_user_can( 'administrator' ) ) {
return $items;
}
$mores = [];
$parents = [];
foreach ( $items as $key => $item ) {
if ( '#' === $item->url && 'More' === $item->title ) {
$mores[] = $item->ID;
}
if ( ! in_array( $item->menu_item_parent, $parents ) ) {
$parents[] = $item->menu_item_parent;
}
}
$mores = array_diff( $mores, $parents );
foreach ( $items as $i => $item ) {
if ( in_array( $item->ID, $mores ) ) {
unset( $items[ $i ] );
}
}
return $items;
}
Just make sure to run the appropriate validation so that the menu items don't get messed, e.g. on the Menus admin screen.
And the two functions can also be combined:
function exclude_menu_items( $items, $menu, $args ) {
if ( current_user_can( 'administrator' ) ) {
return $items;
}
$mores = [];
$parents = [];
foreach ( $items as $key => $item ) {
if ( $page = get_post( $item->object_id ) ) {
// ... your code here.
}
if ( ! empty( $items[ $key ] ) ) {
if ( '#' === $item->url && 'More' === $item->title ) {
$mores[] = $item->ID;
}
if ( ! in_array( $item->menu_item_parent, $parents ) ) {
$parents[] = $item->menu_item_parent;
}
}
}
$mores = array_diff( $mores, $parents );
foreach ( $items as $i => $item ) {
if ( in_array( $item->ID, $mores ) ) {
unset( $items[ $i ] );
}
}
return $items;
}
And actually — when wp_nav_menu()
is called — with the wp_nav_menu_objects
hook, it's simpler to remove the "More" items which have no children:
add_filter( 'wp_nav_menu_objects', function ( $items ) {
if ( is_admin() ) { // sample validation..
return $items;
}
return array_filter( $items, function ( $item ) {
return ! ( '#' === $item->url && 'More' === $item->title &&
! in_array( 'menu-item-has-children', $item->classes )
);
} );
} );
Here you may find a more in depth approach on dealing with the limitations of the wp_get_nav_menu_items
It uses SQL to determine the children but provides far more flexibility in order to achieve full control of what you are actually want to do:
//this query produces all you need to have for the children at any depth. It is explained on the readme and will spare you the add on walkers.
$query = "SELECT e.id, e.post_title, CASE WHEN c.meta_value='custom' THEN ( d.meta_value) ELSE concat ('/', e.post_name ,'/') END as linkurl, b.post_id
FROM wp_postmeta a FORCE INDEX (post_id)
JOIN wp_postmeta b FORCE INDEX (post_id) ON a.post_id=b.post_id
JOIN wp_postmeta c FORCE INDEX (post_id) ON c.post_id=b.post_id
JOIN wp_postmeta d FORCE INDEX (post_id) ON d.post_id=b.post_id
JOIN wp_posts e ON e.id=b.meta_value
WHERE d.meta_key='_menu_item_url' AND c.meta_key='_menu_item_object' AND b.meta_key='_menu_item_object_id'
AND a.meta_key='_menu_item_menu_item_parent' AND a.meta_value='" . $submenu->ID . "'";
$resultsData = $wpdb->get_results($query);
foreach ($resultsData as $childrenData) {
$menu_html_s .= '<!-- Lets print each of the children -->';
$menu_html_s .= '<div class="menu-sub menu-sub-accordion menu-active-bg">';
$menu_html_s .= '<div class="menu-item">';
$menu_html_s .= '<a class="menu-link" href="' . $childrenData->linkurl . '">';
$menu_html_s .= '<span class="menu-bullet">';
$menu_html_s .= '<span class="bullet bullet-dot"></span>';
$menu_html_s .= '</span>';
$menu_html_s .= '<span class="menu-title">' . $childrenData->post_title . '</span>';
$menu_html_s .= '</a>';
$menu_html_s .= '</div>';
$menu_html_s .= '</div>';
More details here:
https://github/exonianp/wp-bootstrap5-accordeon-vertical-menu
本文标签: Search if menu item has child in wpgetnavmenuitems hook
版权声明:本文标题:Search if menu item has child in wp_get_nav_menu_items hook 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.betaflare.com/web/1741534740a2383963.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论