Custom taxonomy term in WooCommerce product permalink

I’ve created a custom taxonomy for the product post type created by WooCommerce. I want to use this taxonomy to the permalinks of the products.

I stumbled upon this which seemed to be perfect for what I wanted to do.
However, I’m having little luck getting it to work. Adding the code in the post I still end up with %item% (which I’m using, rather than %artist%) rather than the term in my permalink.

<?php
/*
Plugin Name:  Woocommerce Item Taxonomy
Description:  Enables a Taxonomy called "Item" for organizing multiple single products under an unified item.
Version:      1.0.0
License:      MIT License
*/

add_action( 'init', 'ps_taxonomy_item' );
function ps_taxonomy_item()  {
  $labels = array(
    'name'                       => 'Item',
    'singular_name'              => 'Item',
    'menu_name'                  => 'Item',
    'all_items'                  => 'All Items',
    'parent_item'                => 'Parent Item',
    'parent_item_colon'          => 'Parent Item:',
    'new_item_name'              => 'New Item Name',
    'add_new_item'               => 'Add New Item',
    'edit_item'                  => 'Edit Item',
    'update_item'                => 'Update Item',
    'separate_items_with_commas' => 'Separate Item with commas',
    'search_items'               => 'Search Items',
    'add_or_remove_items'        => 'Add or remove Items',
    'choose_from_most_used'      => 'Choose from the most used Items',
  );

  $args = array(
    'labels'                     => $labels,
    'hierarchical'               => true,
    'public'                     => true,
    'show_ui'                    => true,
    'show_admin_column'          => true,
    'show_in_nav_menus'          => true,
    'show_tagcloud'              => true,
  );

  register_taxonomy( 'item', 'product', $args );
  register_taxonomy_for_object_type( 'item', 'product' );
}


/**
 * replace the '%item%' tag with the first 
 * term slug in the item taxonomy
 * 
 * @wp-hook post_link
 * @param string $permalink
 * @param WP_Post $post
 * @return string
 */
add_filter( 'post_link', 'wpse_56769_post_link', 10, 2 );
function wpse_56769_post_link( $permalink, $post ) {
    $default_term = 'no_item';
    $terms = wp_get_post_terms( $post->ID, 'item' );

    if ( ! empty( $terms ) && ! is_wp_error( $terms ) )
        $term = current( $terms )->slug;
    else
        $term = $default_term;

    $permalink = str_replace( '%item%', $term, $permalink );

    return $permalink;
}


/**
 * set WP_Rewrite::use_verbose_page_rules to TRUE if %item%
 * is used as the first rewrite tag in post permalinks
 * 
 * @wp-hook do_parse_request
 * @wp-hook page_rewrite_rules
 * @global $wp_rewrite
 * @param mixed $pass_through (Unused)
 * @return mixed
 */
function wpse_56769_rewrite_verbose_page_rules( $pass_through = NULL ) {

    $permastruct = $GLOBALS[ 'wp_rewrite' ]->permalink_structure;
    $permastruct = trim( $permastruct, '/%' );
    if ( 0 !== strpos( $permastruct, 'item%' ) )
        return $pass_through;

    $GLOBALS[ 'wp_rewrite' ]->use_verbose_page_rules = TRUE;
    return $pass_through;
}
add_filter( 'page_rewrite_rules', 'wpse_56769_rewrite_verbose_page_rules', PHP_INT_MAX );
add_filter( 'do_parse_request',  'wpse_56769_rewrite_verbose_page_rules', PHP_INT_MAX );


/**
 * check for existing item and set query to 404 if necessary
 *
 * @wp-hook parse_query
 * @param array $request_vars
 * @return array
 */
function wpse_56769_request_vars( $request_vars ) {

    if ( ! isset( $request_vars[ 'item' ] ) )
        return $request_vars;

    if ( ! isset( $request_vars[ 'name' ] ) )
        return $request_vars;

    if ( 'no_item' == $request_vars[ 'item' ] )
        unset( $request_vars[ 'item' ] );

    return $request_vars;
}
add_filter( 'request', 'wpse_56769_request_vars' );

2 Answers
2

As always it was too simple in the end, was using the wrong hook. post_link is for posts only should use post_type_link instead.

/**
 * replace the '%item%' tag with the first 
 * term slug in the item taxonomy
 * 
 * @wp-hook post_link
 * @param string $permalink
 * @param WP_Post $post
 * @return string
 */
add_filter( 'post_type_link', 'wpse_56769_post_link', 10, 2 );
function wpse_56769_post_link( $permalink, $post ) {

    if( $post->post_type == 'product' ) {
      $default_term = 'no_item';
      $terms = wp_get_post_terms( $post->ID, 'item' );

      if ( ! empty( $terms ) && ! is_wp_error( $terms ) )
          $term = current( $terms )->slug;
      else
          $term = $default_term;

      $permalink = str_replace( '%item%', $term, $permalink );
    }

    return $permalink;
}

Leave a Comment