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;
}
Related
I need to see the categories in the body to call certain CSS. I tried to add a second category term to the allready excisting working code. But it won't work for the newly added category (portfolio_category). What am I doing wrong?
//add taxonomy to body class
add_filter( 'body_class', 'themeprefix_add_taxonomy_class' );
// Add taxonomy terms name to body class
function themeprefix_add_taxonomy_class( $classes ){
if( is_singular() ) {
global $post;
$taxonomy_terms = get_the_terms($post->ID, 'arbocategory', 'portfolio_category' );
if ( $taxonomy_terms ) {
foreach ( $taxonomy_terms as $taxonomy_term ) {
$classes[] = 'tax_' . $taxonomy_term->slug;
}
}
}
return $classes;
}
https://developer.wordpress.org/reference/functions/get_the_terms/
get_the_terms() accepst two paramters $post, $taxonomy, you can't just dump multiple taconomies to the function and expect it to work. you need to run get_the_terms() twice.
function themeprefix_add_taxonomy_class( $classes ){
if( is_singular() ) {
global $post;
$terms = array('arbocategory', 'portfolio_category');
foreach ($terms as $t) {
$taxonomy_terms = get_the_terms($post->ID, $t );
if (is_array($taxonomy_terms) && count($taxonomy_terms)>0) {
foreach ( $taxonomy_terms as $taxonomy_term ) {
$classes[] = 'tax_' . $taxonomy_term->slug;
}
}
}
}
return $classes;
}
Please pardon me if there's a better way to do this as I am not very familiar with this code. I would like to display only the link to the homepage and the current product on the breadcrumb.
Desire result:
Currently:
I found the code for the breadcrumb, is there a way to only display the first and last crumb regardless of the hierarchy?
foreach ( $breadcrumb as $key => $crumb ) {
echo $before;
if ( ! empty( $crumb[1] ) && sizeof( $breadcrumb ) !== $key ) {
echo '' . esc_html( $crumb[0] ) . '';
} else if(!is_product() && !flatsome_option('wc_category_page_title')) {
echo esc_html( $crumb[0] );
}
echo $after;
// Single product or Active title
if(is_product() || flatsome_option('wc_category_page_title')){
$key = $key+1;
if ( sizeof( $breadcrumb ) > $key) {
echo ' <span class="divider">'.$delimiter.'</span> ';
}
} else{
// Category pages
if ( sizeof( $breadcrumb ) !== $key + 1 ) {
echo ' <span class="divider">'.$delimiter.'</span> ';
}
}
}
The reason why I am doing this is that some of the products have multiple categories and by default, it will only show the breadcrumb for the primary category. I would rather make a truncated version as suggested by the owner.
I was also wondering if I can simply dynamically retrieve the product title and link + static homepage link, make it into a shortcode so that I can paste it in the product page.
Hi – the first example in the answer above, also removes the shop from woocommerce breadcrumb. Here is a working example, that only removes the category:
// remove only the category from woocommerce breadcrumbs
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
//print the array and look for the key with the category
//echo '<pre>';
//print_r($crumbs);
//echo '</pre>';
//unset($crumbs[2]); in my case it is key 2
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
} else {
unset($crumbs[2]); // this isn't enough, it would leave a trailing delimiter
$newBreadC = array_values($crumbs); //therefore create new array
return $newBreadC; //return the new array
}
}
If you want to remove categories and subcategories from product breadcrumbs on the product page you can use the woocommerce_get_breadcrumb hook.
// change the breadcrumb on the product page
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
}
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
return $new_crumbs;
}
The code has been tested and works. Add it to your active theme's functions.php.
A good alternative is to set the primary product category for each product. You can do this by installing the Yoast SEO plugin.
You can use the _yoast_wpseo_primary_product_cat product meta to set the id of the primary product category.
After setting the primary category id by editing the product in the
backend or importing a .csv file you will only need to change the
permalink and breadcrumbs based on the primary product category.
To update the product permalink:
// update the product permalink based on the primary product category
add_filter( 'wc_product_post_type_link_product_cat', 'change_product_permalink_by_cat', 10, 3 );
function change_product_permalink_by_cat( $term, $terms, $post ) {
// get the primary term as saved by Yoast
$primary_cat_id = get_post_meta( $post->ID, '_yoast_wpseo_primary_product_cat', true );
// if there is a primary and it's not currently chosen as primary
if ( $primary_cat_id && $term->term_id != $primary_cat_id ) {
// find the primary term in the term list
foreach ( $terms as $term_key => $term_object ) {
if ( $term_object->term_id == $primary_cat_id ) {
// return this as the primary term
$term = $terms[ $term_key ];
break;
}
}
}
return $term;
}
To update the product breadcrumbs on the product page:
// change the breadcrumb on the product page
add_filter( 'woocommerce_get_breadcrumb', 'custom_breadcrumb', 20, 2 );
function custom_breadcrumb( $crumbs, $breadcrumb ) {
// only on the single product page
if ( ! is_product() ) {
return $crumbs;
}
global $product;
$new_crumbs = array();
if ( $product->get_meta( '_yoast_wpseo_primary_product_cat', true ) ) {
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the id of the primary product category
$primary_cat_id = $product->get_meta( '_yoast_wpseo_primary_product_cat', true );
// create an array with all parent categories (based on the id of the primary product category)
$parent_categories = get_ancestors( $primary_cat_id, 'product_cat' );
$parent_categories = array_reverse($parent_categories);
$parent_categories[] = $primary_cat_id;
// for each product category it gets the name and the permalink
foreach ( $parent_categories as $cat_id ) {
$term = get_term_by( 'id', $cat_id, 'product_cat' );
$new_crumbs[] = array(
0 => $term->name,
1 => esc_url( get_term_link( $term, 'product_cat' ) )
);
}
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
} else {
// gets the first element of the array "$crumbs"
$new_crumbs[] = reset( $crumbs );
// gets the last element of the array "$crumbs"
$new_crumbs[] = end( $crumbs );
}
return $new_crumbs;
}
The code has been tested and works. Add it to your active theme's functions.php.
I have created a template named category-south-africa.php. I would like all child, grandchild, great grandchild categories to automatically use this template. For example, all these categories must use the category-south-africa.php template:
/category/south-africa/
/category/south-africa/stellenbosch/
/category/south-africa/stellenbosch/wine-farm/
/category/south-africa/stellenbosch/wine-farm/wine-varietal/
Does anyone perhaps have a php function solution?
You can create you own template for this. You can check the category_template filter and do something like this.
add_filter('category_template', 'add_top_term_template');
function add_top_term_template($template) {
$term = get_queried_object();
$parent = $term->parent;
if($parent === 0) {
return $template;
}
$topLevel = null;
while($parent !== 0) {
$topLevel = get_term($parent);
$parent = $topLevel->parent;
}
$templates = ["term-top-level-{$topLevel->slug}.php"];
return locate_template($templates);
}
You can find more info about the filter here: https://codex.wordpress.org/Plugin_API/Filter_Reference/category_template
get_ancestors() that returns an array containing the parents of any given category.
use like get_ancestors( child_category_id, 'category' );
Check the value is in the array or not. if yes then define template name.
You can use this function to check if the current category is a child category and set the template to the same you have for the parent category. Put this in the functions.php of your theme.
function childcategory_template( $template ) {
$cat = get_queried_object();
if( 0 < $cat->category_parent )
$template = locate_template( 'category-south-africa.php');
return $template;
}
add_filter( 'category_template', 'childcategory_template' );
Edit after comment: You can also check the name (slug) and set the $template variable taking the name (slug) of the parent into account.
function childcategory_template( $template ) {
$cat = get_queried_object();
if( 0 < $cat->category_parent )
$parent_id = $cat->category_parent;
if ( get_category( $parent_id )->slug == "south-africa" ) {
$template = locate_template( 'category-south-africa.php');
} else if ( get_category( $parent_id )->slug == "north-africa" ) {
$template = locate_template( 'category-north-africa.php');
}
return $template;
}
add_filter( 'category_template', 'childcategory_template' );
I need to filter the wordpress-menu by category name.
What I found so far is the following loop, which gives me all menu-items:
function exclude_menu_items( $items, $menu, $args ) {
// Iterate over the items to search and destroy
foreach ( $items as $key => $item ) {
//if ( $item->object_id == 168 ) unset( $items[$key] );
print_r($item);
echo '<br>';
//var_dump(get_the_category($item->ID));
//echo get_query_var('cat');
}
return $items;
}
add_filter( 'wp_get_nav_menu_items', 'exclude_menu_items', null, 3 );
I added the capability for pages to have categories like this:
function add_categories_for_pages() {
register_taxonomy_for_object_type('category', 'page');
}
add_action( 'init', 'add_categories_for_pages' );
But now how can I get the pages category from a menu item?
Seems I have found a solution, I don't know if it's the best but so far it seems to work:
function exclude_menu_items( $items, $menu, $args ) {
// Iterate over the items to search and destroy
foreach ( $items as $key => $item ) {
$categories = get_the_category( $item->object_id );
foreach($categories as $category){
if($category->cat_name != 'mycatname'){
unset( $items[$key] );
}
}
}
return $items;
}
What do you think?
Good morning, I'm trying to change the header depending on the category of the single product. i am using Wordpress & WooCommerce
My product categories look like this
- the-lawn-store
- - turf
- - grass-seed
- - wildflower-turf
- the-oak-store
- - railway-sleepers
- - pergolas
Basically when viewing an item which falls under the parent category of the-lawn-store I need the header to be <?php get_header('lawn'); ?> and when the parent category is the-oak-store I need the header to be <?php get_header('oak'); ?>, the difference between headers is the styling of the over all page! what is the best way to go about this?
Well, what you need is the category parent. In order to do that, first of all you can get the parent ID with this:
global $wp_query;
$cat_obj = $wp_query->get_queried_object();
if($cat_obj) {
//print_r($cat_obj);
$category_ID = $cat_obj->term_id;
$category_parent = $cat_obj->parent;
$category_taxonomy = $cat_obj->taxonomy;
$category_parent_term = get_term_by( 'id', absint( $category_ID ), $category_taxonomy );
$category_parent_slug = $category_parent_term->slug;
get_header( $category_parent_slug );
}else{
get_header();
}
Uncomment the print_r to see the rest of available vars. Tested on my local woo and works.
You can't filter the get_header() function so you will have to override WooCommerce's single-product.php template. From there you can modify the beginning of the file:
get_header( 'shop' ); ?>
I created the following function to get the top-level product category for any product:
function kia_get_the_top_level_product_category( $post_id = null ){
$product_cat_parent = null;
if( ! $post_id ){
global $post;
$post_id = $post->ID;
}
// get the product's categories
$product_categories = get_the_terms( $product_id, 'product_cat' );
if( is_array( $product_categories ) ) {
// gets complicated if multiple categories, so limit to one
// on the backend you can restrict to a single category with my Radio Buttons for Taxonomies plugin
$product_cat = array_shift( $product_categories);
$product_cat_id = $product_cat->term_id;
while ($product_cat_id) {
$cat = get_term($product_cat_id, 'product_cat'); // get the object for the product_cat_id
$product_cat_id = $cat->parent; // assign parent ID (if exists) to $product_cat_id
// the while loop will continue whilst there is a $product_cat_id
// when there is no longer a parent $product_cat_id will be NULL so we can assign our $product_cat_parent
$product_cat_parent = $cat->slug;
}
}
return $product_cat_parent;
}
Then in your theme's single-product.php you could do:
$parent = kia_get_the_top_level_product_category();
if( $parent == 'oak' ){
get_header('oak');
} elseif( $parent == 'lawn' ){
get_header('lawn');
} else {
get_header('shop');
}
If you don't have a specific header-shop.php then you could technically also do:
$parent = kia_get_the_top_level_product_category();
get_header( $parent );
Overriding this template might put you at risk when WooCommerce upgrades. As an alternative I would suggest filtering the body class.
function wpa_22066003_body_class($c){
if( function_exists('is_product') && is_product() && $parent = kia_get_the_top_level_product_category() ){
$c[] = $parent . '-product-category';
}
return $c;
}
add_filter( 'body_class', 'wpa_22066003_body_class' );