-
-
Save joshuadavidnelson/282614233c25071669f2502d77a7a8f9 to your computer and use it in GitHub Desktop.
<?php | |
/** | |
* Plugin Name: Duplicated Child Term Url Slugs | |
* Description: Duplicate child term urls slugs in hierarchical taxonomies. | |
* Version: 0.1.0 | |
* Author: joshuadnelson | |
* License: GPLv2 or later | |
* License URI: https://www.gnu.org/licenses/gpl-2.0.html | |
* | |
* This is setup like a plugin, but can be included in a theme or as a class in a plugin. | |
* | |
* Using a Parent > Child category structure with duplicate child url slugs; | |
* for urls like `category/parent-1/child` and `category/parent-2/child`. | |
* | |
* Child term slugs must be unique per WordPress core requirements. | |
* In order to have duplicated child url slugs, the term slug must follow | |
* a pattern we can transform to tell WordPress how to route the url. | |
* | |
* Then apply a custom rewrite rule to match the pattern and filter the | |
* term link to remove the parent term slug from the child term url. | |
* | |
* Here the child slugs are structured like "{parent-slug}_{child-url-slug}" | |
* and the term link is shown as `category/{parent-slug}/{child-url-slug}`. | |
* | |
* Examples: | |
* - url `example.url/category/books/hobbit/` | |
* for the `books_hobbit` child term and `books` parent term | |
* - url `example.url/category/movies/hobbit/` | |
* for the `movies_hobbit` child term and `movies` parent term | |
* | |
* Notes: | |
* - Permalinks need to be "pretty" and need to be flushed after apply these changes. | |
* - Parent terms must be unique, child terms can be duplicated. | |
* - Terms cannot have a "_" in a slug other than for this purpose. | |
* - Child terms must contain the parent term slug, be careful if you change those on | |
* the parent term you will need to update the child term slugs as well. | |
* - Only works for hierarchical taxonomies, specifically the built-in 'category' taxonomy. | |
* You can modify the taxonomy name in the code to work with other taxonomies | |
* by changing the 'category' taxonomy name and the 'category_name' query var. | |
*/ | |
/** | |
* Main class for the Duplicated Child Url Slugs plugin. | |
* | |
* @since 0.1.0 | |
*/ | |
class DuplicatedChildUrlSlugs { | |
/** | |
* The taxonomy name. | |
* | |
* @since 0.1.0 | |
* @var string | |
*/ | |
protected $taxonomy = 'category'; | |
/** | |
* The taxonomy query var. | |
* | |
* @since 0.1.0 | |
* @var string | |
*/ | |
protected $query_var = 'category_name'; | |
/** | |
* The separator used to separate parent and child term slugs. | |
* | |
* @since 0.1.0 | |
* @var string | |
*/ | |
protected $sep = '_'; | |
/** | |
* Register the plugin | |
* | |
* @since 0.1.0 | |
* @return void | |
*/ | |
public function register() { | |
// Filter the term link to handle hierarchical taxonomy permalinks. | |
add_filter( 'term_link', array( $this, 'term_link' ), 10, 3 ); | |
// Parse the query to handle hierarchical taxonomy permalinks. | |
add_action( 'parse_query', array( $this, 'parse_query' ) ); | |
// Add custom rewrite rules for parent/child category urls. | |
add_action( 'init', array( $this, 'rewrite_rules' ), 100 ); | |
} | |
/** | |
* Add custom rewrite rules for parent/child category urls. | |
* | |
* Adds a rewrite rule that will match terms | |
* that have their slug prefixed with the parent term | |
* slug, separated by an underscore, but their url | |
* is just the child term slug. | |
* | |
* @since 0.1.0 | |
* @return void | |
*/ | |
function rewrite_rules() { | |
$tax_obj = get_taxonomy( $this->taxonomy ); | |
if ( $tax_obj ) { | |
$tax_slug = $tax_obj->rewrite['slug']; | |
add_rewrite_rule( | |
$tax_obj->rewrite['slug'] . '/([^/]+)/([^/]+)/?', | |
'index.php?' . $tax_obj->query_var . '=$matches[1]_$matches[2]', | |
'top' | |
); | |
} | |
} | |
/** | |
* Filter the term link to handle hierarchical taxonomy permalinks. | |
* | |
* This will filter the term link to remove the parent term slug | |
* from the child term url, so that the url is just the child term slug, | |
* where the child term name is "parent_child" and the parent term name is "parent". | |
* | |
* @since 0.1.0 | |
* @param string $link The term link | |
* @param WP_Term $term The term object | |
* @param string $taxonomy The taxonomy name | |
* @return string | |
*/ | |
function term_link( $link, $term, $taxonomy ) { | |
if ( $taxonomy !== $this->taxonomy ) { | |
return $link; | |
} | |
// Get the parent term | |
$parent_term = get_term( $term->parent, $taxonomy ); | |
if ( $parent_term && ! is_wp_error( $parent_term ) ) { | |
// Remove the "parent_" prefix from the child term slug | |
// to generate a url that looks like /parent/child/ | |
$link = str_replace( '/' . $term->slug, '/' . str_replace ( $parent_term->slug . '_', '', $term->slug ), $link ); | |
} | |
return $link; | |
} | |
/** | |
* Parse the query to handle hierarchical taxonomy permalinks. | |
* | |
* This filter the query to check for terms that are child terms | |
* yet do not have the parent term in the slug, thus the parent/child | |
* structure should literally look for a "child" slug, not "parent_child" | |
* | |
* @since 0.1.0 | |
* @param WP_Query $query The WP_Query instance (passed by reference) | |
* @return void | |
*/ | |
function parse_query( $query ) { | |
// Check if we have the taxonomy query var set. | |
if ( isset( $query->query_vars[ $this->query_var ] ) ) { | |
// If we have a term slug with a separator, check if it's a child term | |
$slug = $query->query_vars[ $this->query_var ]; | |
if ( strpos( $slug, $this->sep ) !== false ) { | |
// Separate the parent and child term slugs. | |
list( $parent, $child ) = explode( $this->sep, $slug, 2 ); | |
// If the parent-child term does not exist, but the child term | |
// slug as-is is valid, then set the query var to the child term. | |
if ( ! term_exists( $slug, $this->taxonomy ) && term_exists( $child, $this->taxonomy ) ) { | |
$query->query_vars[ $this->query_var ] = $child; | |
} | |
} | |
} | |
} | |
} | |
$dup_url_slugs = new DuplicatedChildUrlSlugs(); | |
$dup_url_slugs->register(); |
Hello, first of all, thank you for your plugin. I'm trying to do it for woocommerce product categories but I couldn't do it. Can you help me? Example of the URL structure I want to use
curtain/rooms/living-room/
wall-murals/rooms/living-room/
Have a nice day
Hey @sergenadiyaman. You're example needs to account for two modifications: a custom taxonomy and another level of duplication in the url slug - nesting 3-terms deep instead of 2-terms deep.
Custom taxonomy: product_cat
This example is for the built-in category
taxonomy, you'll need to make some changes to use this on custom taxonomies.
I believe WooCommerce "product categories" are registered as product_cat
, so you'll first want set $taxonomy = 'product_cat'
and also $query_var = 'product_cat'
(I'm not 100% sure on the WooCommerce naming for this, but the taxonomy slug is usually the default query_var).
Additional level to the url pattern
This is more complicated. You'll also need to update this to support 3-levels of nested permalink structure, this gist adds support for two levels (/parent/child/
) but your example has three (/grandparent/parent/child/
), which means the rewrite rule needs to look at three different parts of the link and you'll need a consistent term slug pattern to match to the correct grandparent, parent, and child terms.
For instance you may need to name each of those terms as:
curtain
+curtain_rooms
+curtain_rooms_living-room
to support a url like/curtain/rooms/living-room/
wall-murals
+wall-murals_rooms
+wall-murals_rooms_living-room
to support a url likewall-murals/rooms/living-room/
...and then parse that permalink to match the terms accordingly, some like this (warning I have not tested this):
function rewrite_rules() {
$tax_obj = get_taxonomy( $this->taxonomy );
if ( $tax_obj ) {
$tax_slug = $tax_obj->rewrite['slug'];
add_rewrite_rule(
$tax_obj->rewrite['slug'] . '/([^/]+)/([^/]+)/([^/]+)/?',
'index.php?' . $tax_obj->query_var . '=$matches[1]_$matches[2]_$matches[3]',
'top'
);
}
}
You'll also need to update the logic in the term_link
filter to look for a grandparent term & update the url. Lastly, the parse_query
would need to be updated as well, to accommodate terms not matching these patterns. Best of luck!
I'm sorry, I'm not a software developer. I'm just trying to make my website more efficient and I discovered your plugin. It works for post types, but I couldn't do it for Woocommerce product categories. I have no idea which taxamony to change or how to update term_link. I would appreciate it if you could write more clearly. Or it would be great if you could make a new plugin like the one you made. Thank you for your help
To get this gist working with WooCommerce you should only need to change two lines:
- Line 55 from
protected $taxonomy = 'category';
toprotected $taxonomy = 'product_cat';
- Line 63 from
protected $query_var = 'category_name';
toprotected $query_var = 'product_cat';
.
However, that will still only work for a 2-level permalink structure like rooms\living-rooms
. Going to a 3-level permalink structure like curtain/rooms/living-room/
is outside of the scope of this plugin as it's currently written.
thanks for your help
For a custom taxonomy, set the
$taxonomy
var as your taxonomy slug and$query_var
with your taxonomy's query variable (seeregister_taxonomy
)