Automatically add WooCommerce product subcategories to categories in menu

add_filter( 'wp_get_nav_menu_items', 'wpcb_add_product_subcats_to_menu' );

if ( ! function_exists( 'wpcb_add_product_subcats_to_menu' ) ) {
	/**
	 * wpcb_add_product_subcats_to_menu.
	 *
	 * @see https://wpcodebook.com/snippets/automatically-add-woocommerce-product-subcategories-to-categories-in-menu/
	 */
	function wpcb_add_product_subcats_to_menu( $items ) {
		if ( is_admin() ) {
			return $items;
		}
		$children_order = count( $items ) + 1;
		foreach ( $items as $item ) {
			$items = wpcb_process_menu_item( $item, $items, $children_order );
		}
		return $items;
	}
}

if ( ! function_exists( 'wpcb_process_menu_item' ) ) {
	/**
	 * wpcb_process_menu_item.
	 */
	function wpcb_process_menu_item( $item, $items, &$children_order ) {
		if ( 'product_cat' === $item->object ) {

			$children = get_terms( array(
				'taxonomy'     => 'product_cat',
				'hide_empty'   => false,
				'parent'       => $item->object_id,
				'limit'        => -1,
				'orderby'      => 'title',
				'order'        => 'ASC',
			) );

			if ( ! empty( $children ) && ! is_wp_error( $children ) ) {
				foreach ( $children as $child ) {

					$tmp_item = new stdClass();

					$tmp_item->ID                      = 100000 + $children_order;
					$tmp_item->post_author             = $item->post_author;
					$tmp_item->post_date               = '';
					$tmp_item->post_date_gmt           = '';
					$tmp_item->post_content            = '';
					$tmp_item->post_title              = '';
					$tmp_item->post_excerpt            = '';
					$tmp_item->post_status             = 'publish';
					$tmp_item->comment_status          = '';
					$tmp_item->ping_status             = '';
					$tmp_item->post_password           = '';
					$tmp_item->post_name               = $child->name;
					$tmp_item->to_ping                 = '';
					$tmp_item->pinged                  = '';
					$tmp_item->post_modified           = '';
					$tmp_item->post_modified_gmt       = '';
					$tmp_item->post_content_filtered   = '';
					$tmp_item->post_parent             = 0;
					$tmp_item->guid                    = '';
					$tmp_item->menu_order              = $children_order;
					$tmp_item->post_type               = 'nav_menu_item';
					$tmp_item->post_mime_type          = '';
					$tmp_item->comment_count           = '';
					$tmp_item->filter                  = 'raw';
					$tmp_item->db_id                   = $tmp_item->ID;
					$tmp_item->menu_item_parent        = $item->ID;
					$tmp_item->object_id               = $child->term_id;
					$tmp_item->object                  = 'product_cat';
					$tmp_item->type                    = 'taxonomy';
					$tmp_item->type_label              = '';
					$tmp_item->url                     = get_term_link( $child->term_id );
					$tmp_item->title                   = $child->name;
					$tmp_item->target                  = '';
					$tmp_item->attr_title              = '';
					$tmp_item->description             = '';
					$tmp_item->classes                 = array( 0 => '' );
					$tmp_item->xfn                     = '';

					$items[] = $tmp_item;
					$children_order++;

					$items = wpcb_process_menu_item( $tmp_item, $items, $children_order );

				}
			}

		}
		return $items;
	}
}

4 Comments

  1. Nadia July 13, 2022 at 11:37 am #

    Thanks for the snippet.
    But how could it be to have the full categories tree and not just the first level?

    • Tom July 14, 2022 at 2:28 am #

      Hi Nadia,

      Happy to help 🙂

      I’ve just updated the snippet for you – now it will include the full categories tree.

      Please give it a try and let me know what you think.

    • Nadia July 18, 2022 at 1:58 pm #

      Thanks a lot! But it is not working as expected.

      With the previous snipped I had several menu items (shop and not), In one of that I had Man And Woman as shop categories and the snipped populated the links UNDER Man and Women with the first level categories.

      Now as a test I created a custom link “test”, inside it I placed the shop category “woman” and I expected to see something like
      – menu link 1
      – menu link 2
      – test
      – – woman
      – – – t-shirt
      – – – sweaters
      and so on.
      While I had directly all the sub categories, with no nidification, in the first level of the menu (deleting the word “test”). Moreover, I applied a CSS to “test” to hide it in test mode, but the class was missing.

      The result is:
      – menu link 1
      – menu link 2
      – t-shirt
      – sweaters

      Thanks

    • Tom July 19, 2022 at 12:01 am #

      Hi Nadia,

      I wasn’t able to reproduce the issue as you’ve described, however, I did find a bug when a category is displayed in the menu several times. Fixed it by replacing:

      $tmp_item->ID = $child->term_id * 10000;

      with:

      $tmp_item->ID = 100000 + $children_order;

      I’ve updated the snippet. Please give it a try and let me know if that helped.

      Regarding “… the class was missing…” – not sure if I got it correctly, but you can add a class to your menu items by replacing:

      $tmp_item->classes = array( 0 => '' );

      with:

      $tmp_item->classes = array( 'myclass1', 'myclass2' );

      Hope this helps. Please let me know if you have any questions.

Leave a Reply