Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save levantoan/fc705c5ae4739e6d87e2ec51b257ea5c to your computer and use it in GitHub Desktop.
Save levantoan/fc705c5ae4739e6d87e2ec51b257ea5c to your computer and use it in GitHub Desktop.
How to set product category base the same as shop base in WooCommerce
<?php
/*
Support WPML - 25/02/2019
/*
function devvn_product_category_base_same_shop_base( $flash = false ){
global $sitepress;
$languages = icl_get_languages('skip_missing=0&orderby=code');
if($languages && !empty($languages)){
$original_lang = ICL_LANGUAGE_CODE;
foreach($languages as $key=>$lang) {
$new_lang = $key;
$sitepress->switch_lang($new_lang);
$terms = get_terms(array(
'taxonomy' => 'product_cat',
'post_type' => 'product',
'hide_empty' => false,
));
if ($terms && !is_wp_error($terms)) {
$siteurl = apply_filters( 'wpml_home_url', get_home_url('/'));
$siteurl = ($sitepress->get_default_language() == $key) ? $siteurl.'/' : $siteurl;
foreach ($terms as $term) {
$term_slug = $term->slug;
$baseterm = str_replace($siteurl, '', get_term_link($term->term_id, 'product_cat'));
add_rewrite_rule($baseterm . '?$', 'index.php?product_cat=' . $term_slug, 'top');
add_rewrite_rule($baseterm . 'page/([0-9]{1,})/?$', 'index.php?product_cat=' . $term_slug . '&paged=$matches[1]', 'top');
add_rewrite_rule($baseterm . '(?:feed/)?(feed|rdf|rss|rss2|atom)/?$', 'index.php?product_cat=' . $term_slug . '&feed=$matches[1]', 'top');
}
}
$sitepress->switch_lang($original_lang);
}
}
if ($flash == true)
flush_rewrite_rules(false);
}
add_filter( 'init', 'devvn_product_category_base_same_shop_base');
/*Sửa lỗi khi tạo mới taxomony bị 404*/
add_action( 'create_term', 'devvn_product_cat_same_shop_edit_success', 10, 2 );
function devvn_product_cat_same_shop_edit_success( $term_id, $taxonomy ) {
devvn_product_category_base_same_shop_base(true);
}
<?php
/*
***** Into:
First, Go to Setting / Permalinks:
Shop base: shop
Product category base: shop (same as shop base)
Product permalink base: Shop base with category, e.g. shop/%product_cat%
***** Then, We have
Shop page: http://domain.com/shop/
Category page: http://domain.com/shop/computer
Product page: http://domain.com/shop/computer/dell-vostro-5459
***** But It 404 error at "Category page"
***** Don't worry, I fixed it by code below. Insert it into functions.php in your theme.
***** Post link
http://levantoan.com/cach-cai-dat-base-cua-danh-muc-san-pham-giong-voi-base-cua-trang-san-pham/
*/
//base product category same base shop Page for woocommerce
function devvn_product_category_base_same_shop_base( $flash = false ){
$terms = get_terms(array(
'taxonomy' => 'product_cat',
'post_type' => 'product',
'hide_empty' => false,
));
if ($terms && !is_wp_error($terms)) {
$siteurl = esc_url(home_url('/'));
foreach ($terms as $term) {
$term_slug = $term->slug;
$baseterm = str_replace($siteurl, '', get_term_link($term->term_id, 'product_cat'));
add_rewrite_rule($baseterm . '?$','index.php?product_cat=' . $term_slug,'top');
add_rewrite_rule($baseterm . 'page/([0-9]{1,})/?$', 'index.php?product_cat=' . $term_slug . '&paged=$matches[1]','top');
add_rewrite_rule($baseterm . '(?:feed/)?(feed|rdf|rss|rss2|atom)/?$', 'index.php?product_cat=' . $term_slug . '&feed=$matches[1]','top');
}
}
if ($flash == true)
flush_rewrite_rules(false);
}
add_filter( 'init', 'devvn_product_category_base_same_shop_base');
/*Sửa lỗi khi tạo mới taxomony bị 404*/
add_action( 'create_term', 'devvn_product_cat_same_shop_edit_success', 10, 2 );
function devvn_product_cat_same_shop_edit_success( $term_id, $taxonomy ) {
devvn_product_category_base_same_shop_base(true);
}
Copy link

ghost commented Jun 20, 2017

Works as needed, no issues noticed so far. thanks a bunch!

@ucu2009
Copy link

ucu2009 commented Aug 25, 2017

thank you for the function, it work's for category page, but for product page i still see the 404 error

@daron4ever
Copy link

Thanks but I also have 404 error at product page.

@OlegApanovich
Copy link

OlegApanovich commented Apr 4, 2018

These code works great for me but it seems to me that I found mistake in your code in line
$new_rules[$baseterm.'page/([0-9]{1,})/?$'] = 'index.php?product_cat='.$term_slug.'&paged=$matches[1]';
There is definitely should be
$new_rules[$baseterm.'/page/([0-9]{1,})/?$'] = 'index.php?product_cat='.$term_slug.'&paged=$matches[1]';
Or pagination for a product-category goes to 404 error.
And I don't know why there is so complicated code when you create new term like for me there is enough to just flush permalinks.
There is working final version of working code for me

/**
 * Set product category base url same as a shop base
 *
 * @param array $rules
 *
 * @return array
 */
add_filter( 'rewrite_rules_array', function( $rules ) {
	$new_rules = array();
	$terms = get_terms( array(
		'taxonomy'   => 'product_cat',
		'post_type'  => 'product',
		'hide_empty' => false,
	));
	if ( $terms && ! is_wp_error( $terms ) ) {
		$siteurl = esc_url( home_url( '/' ) );
		foreach ( $terms as $term ) {
			$term_slug = $term->slug;
			$baseterm = str_replace( $siteurl, '', get_term_link( $term->term_id, 'product_cat' ) );
			// rules for a specific category
			$new_rules[$baseterm .'?$'] = 'index.php?product_cat=' . $term_slug;
			// rules for a category pagination
			$new_rules[$baseterm . '/page/([0-9]{1,})/?$' ] = 'index.php?product_cat=' . $term_slug . '&paged=$matches[1]';
			$new_rules[$baseterm.'(?:feed/)?(feed|rdf|rss|rss2|atom)/?$'] = 'index.php?product_cat=' . $term_slug . '&feed=$matches[1]';
		}
	}

	return $new_rules + $rules;
} );

/**
 * Flush rewrite rules when create new term
 * need for a new product category rewrite rules
 */
function imp_create_term() {
	flush_rewrite_rules(false);;
}
add_action( 'create_term', 'imp_create_term' );

@plexus77
Copy link

plexus77 commented Apr 20, 2018

There is a simpler way to achieve this...

function devvn_product_category_base_same_shop_base( $flash = false ){
    add_rewrite_rule('^shop/([^/]+)/?$','index.php?product_cat=$matches[1]','top');
    if ($flash == true) {
        flush_rewrite_rules(false);
    }
}
add_filter( 'init', 'devvn_product_category_base_same_shop_base');

add_action( 'create_term', 'devvn_product_cat_same_shop_edit_success', 10, 2 );
function devvn_product_cat_same_shop_edit_success( $term_id, $taxonomy ) {
    devvn_product_category_base_same_shop_base(true);
}

@roofpooper
Copy link

@plexus77
but somehow ur solution breaks the pagination ('.../product_cat/page/2/' returns 404)

@k3nsai
Copy link

k3nsai commented Jun 13, 2018

@levantoan

Both the OLD and the NEW code only work partially. The breadcrumb links do not work.
For example, while site.com/shop/category/product works fine, if you input the url DIRECTLY in the browser you get 404 not found.
Also, if you click on the breadcrumb links you still get 404.

@MrJoshFisher
Copy link

MrJoshFisher commented Jul 25, 2018

@OlegApanovich recent code fixes the 404 when clicking page 2 etc, just need to save permalinks after adding, good job was worried about this ha

@mantis4444
Copy link

Hello, thank you for the solution! I have a question. Maybe you know how to remove shop base from the breadcrumb? That it would be like on default woocommerce (homepage -> product_cat -> product) instead of (homepage -> shop -> product_cat -> product). Again no the url, just the breadcrumbs.

@molod
Copy link

molod commented Nov 21, 2018

Sadly, doesn't work for me. Version 3.5.1

@Swennet
Copy link

Swennet commented Jan 14, 2019

Doesn't work in 3.5.3. Any solution for this?

@arestice
Copy link

Everything works fine except that page 2 or any next page gives 404.
Please, can someone check this?

@janvier005
Copy link

Thanks, this is great and working out of the box !!!

@eppul
Copy link

eppul commented Jun 23, 2020

Hi,

Thank you for the code.

Had to do some changes to get it to work with WPML.

/**
 * Set product category base url same as a shop base
 *
 * @param array $rules
 *
 * @see https://gist.github.com/levantoan/fc705c5ae4739e6d87e2ec51b257ea5c
 * @return array
 */
add_filter( 'rewrite_rules_array',
	function( $rules ) {
		$new_rules = array();
		global $sitepress;
		if ( $sitepress ) {
			// Disable wpml filters to get all the product terms.
			remove_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ) );
			remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ) );
			remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
			// Get the terms.
			$terms = get_terms( array( 'taxonomy' => 'product_cat', 'post_type' => 'product', 'hide_empty' => false ) );
		} else {
			$terms = get_terms( array( 'taxonomy' => 'product_cat', 'post_type' => 'product', 'hide_empty' => false ) );
		}
		// Check if any terms are present and the there are no errors.
		if ( $terms && ! is_wp_error( $terms ) ) {
			foreach ( $terms as $term ) {
				// Detect term language.
				$language_code = apply_filters( 'wpml_element_language_code', null, array( 'element_id' => (int) $term->term_taxonomy_id, 'element_type' => 'product_cat' ) );

				// Check the language code.
				if ( $language_code === 'en' ) {
					$siteurl = esc_url( home_url( '/en/' ) );
				} else {
					$siteurl = esc_url( home_url( '/' ) );
				}
				$term_slug = $term->slug;
				$baseterm = str_replace( $siteurl, '', get_term_link( $term->term_id, 'product_cat' ) );

				// Rules for a specific category.
				$new_rules[ $baseterm . '?$' ] = 'index.php?product_cat=' . $term_slug;
				// Rules for a category pagination.
				$new_rules[ $baseterm . '/page/([0-9]{1,})/?$' ] = 'index.php?product_cat=' . $term_slug . '&paged=$matches[1]';
				$new_rules[ $baseterm . '(?:feed/)?(feed|rdf|rss|rss2|atom)/?$' ] = 'index.php?product_cat=' . $term_slug . '&feed=$matches[1]';
			}
		}
		if ( $sitepress ) {
			add_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ), 10, 4 );
			add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
			add_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ), 10, 2 );
		}
		return $new_rules + $rules;
	}
);

/**
 * Flush rewrite rules when create new term
 * need for a new product category rewrite rules
 *
 */
function imp_create_term() {
	flush_rewrite_rules( false );
}
add_action( 'create_term', 'imp_create_term' );

@levantoan
Copy link
Author

levantoan commented Jun 23, 2020

Hi,

Thank you for the code.

Had to do some changes to get it to work with WPML.

/**
 * Set product category base url same as a shop base
 *
 * @param array $rules
 *
 * @see https://gist.github.com/levantoan/fc705c5ae4739e6d87e2ec51b257ea5c
 * @return array
 */
add_filter( 'rewrite_rules_array',
	function( $rules ) {
		$new_rules = array();
		global $sitepress;
		if ( $sitepress ) {
			// Disable wpml filters to get all the product terms.
			remove_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ) );
			remove_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ) );
			remove_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ) );
			// Get the terms.
			$terms = get_terms( array( 'taxonomy' => 'product_cat', 'post_type' => 'product', 'hide_empty' => false ) );
		} else {
			$terms = get_terms( array( 'taxonomy' => 'product_cat', 'post_type' => 'product', 'hide_empty' => false ) );
		}
		// Check if any terms are present and the there are no errors.
		if ( $terms && ! is_wp_error( $terms ) ) {
			foreach ( $terms as $term ) {
				// Detect term language.
				$language_code = apply_filters( 'wpml_element_language_code', null, array( 'element_id' => (int) $term->term_taxonomy_id, 'element_type' => 'product_cat' ) );

				// Check the language code.
				if ( $language_code === 'en' ) {
					$siteurl = esc_url( home_url( '/en/' ) );
				} else {
					$siteurl = esc_url( home_url( '/' ) );
				}
				$term_slug = $term->slug;
				$baseterm = str_replace( $siteurl, '', get_term_link( $term->term_id, 'product_cat' ) );

				// Rules for a specific category.
				$new_rules[ $baseterm . '?$' ] = 'index.php?product_cat=' . $term_slug;
				// Rules for a category pagination.
				$new_rules[ $baseterm . '/page/([0-9]{1,})/?$' ] = 'index.php?product_cat=' . $term_slug . '&paged=$matches[1]';
				$new_rules[ $baseterm . '(?:feed/)?(feed|rdf|rss|rss2|atom)/?$' ] = 'index.php?product_cat=' . $term_slug . '&feed=$matches[1]';
			}
		}
		if ( $sitepress ) {
			add_filter( 'terms_clauses', array( $sitepress, 'terms_clauses' ), 10, 4 );
			add_filter( 'get_term', array( $sitepress, 'get_term_adjust_id' ), 1, 1 );
			add_filter( 'get_terms_args', array( $sitepress, 'get_terms_args_filter' ), 10, 2 );
		}
		return $new_rules + $rules;
	}
);

/**
 * Flush rewrite rules when create new term
 * need for a new product category rewrite rules
 *
 */
function imp_create_term() {
	flush_rewrite_rules( false );
}
add_action( 'create_term', 'imp_create_term' );

Thanks for your code. And you can try this code. does it work? https://gist.github.com/levantoan/fc705c5ae4739e6d87e2ec51b257ea5c#file-support-wpml-set_product_category_base_same_shop_base-php

@PipoSergio
Copy link

Hello @levantoan, I'm trying to make it work at my store with WPML.

The main language is working fine but second is getting 404 error on categories. Could you help me to fix it? Thanks.

@jmvn
Copy link

jmvn commented Jan 7, 2021

@PipoSergio Have you updated your secondary language codes to match the if statement after "// Check the language code."

So if French is a secondary language change it to:
if ( $language_code === 'fr' ) {
$siteurl = esc_url( home_url( '/fr/' ) );

@PipoSergio
Copy link

@PipoSergio Have you updated your secondary language codes to match the if statement after "// Check the language code."

So if French is a secondary language change it to:
if ( $language_code === 'fr' ) {
$siteurl = esc_url( home_url( '/fr/' ) );

No, but because my main language is spanish and the second one is english, so I think I do not have to change anything, right?

@jmvn
Copy link

jmvn commented Jan 7, 2021

Yes then it should work just like it is. Did you try resaving your permalinks and clearing cache. I also had some issue before doing that.

@jmvn
Copy link

jmvn commented Jan 19, 2021

After doing some more testing, I also started having issues with 404s with the code above. I found this that, fingers crossed, seems to work better, both languages and pagination work for me: https://wordpress.org/support/topic/seo-url-for-category-and-product-pages/#post-12571113

@defive
Copy link

defive commented Mar 11, 2021

For anyone still struggling to get pagination working, if you have a line that looks like this:

add_rewrite_rule($baseterm . '/page/([0-9]{1,})/?$', 'index.php?product_cat=' . $term_slug . '&paged=$matches[1]','top');

Change it to:

add_rewrite_rule($baseterm . 'page/([0-9]{1,})/?$', 'index.php?product_cat=' . $term_slug . '&paged=$matches[1]','top');

This made it work for me.

@sykoseksi
Copy link

Came across this issue by chance. If you enter the wrong category, it still works instead of 404'ing like it should
For example /shop/summer/hats/
also works as
/shop/summer-time/hats/

@jmvn
Copy link

jmvn commented Sep 28, 2022

After running this on a production shop for about a year I would recommend against it. I had a lot of weird 404 errors, broken styling and performance issues that all seemed to have gone away when we got rid of this. Some people seem to have had better luck but just wanted to share my perspective, could be something with our hosting that didn't play right.

@sykoseksi
Copy link

sykoseksi commented Sep 28, 2022

I've had no issues running this at all apart from the categories for one site reporting "Alternate page with proper canonical tag" which led me to discover the issue.
I'd just like for this implementation to be a bit stricter on URL's. Other implementations of this also have the same issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment