1. Home
  2. Docs
  3. Filters & Hooks
  4. Conditional permalinks for posts, pages or custom post types

Conditional permalinks for posts, pages or custom post types

Natively WordPress with fixed rewrite rules does not allow to set-up more complicated permalink structures. It is pretty difficult to make the URLs dynamic and adjust them according to requested condition.

You can find more detailed description here.
The default permalinks mentioned in this article are used only for the new posts & terms permalinks. To apply the new permalink formats to existing items please use "Regenerate/reset" functionality.

Table of contents

    Permalink Manager allows to adjust the permastructures programmatically to change the default permalinks conditionally with

    permalink_manager_filter_default_post_uri($default_uri, $native_slug, $post, $slug, $native_uri)

    filter and 5 arguments passed with the hook:

    • $default_uri is a final URI applied to the new posts or the old posts (after the permalinks are regenerated/reset)
    • $native_slug is a native, unfiltered post’s slug (it may differ from slug defined with $slug variable if “Force custom slugs” is enabled in Permalink Manager settings)
    • $post is a post object, an instance of WP_Post class
    • $slug is a slug used normally by Permalink Manager
    • $native_uri, you should check in your custom code snippet if this variable is set to false (see examples below)

    It might be extremely helpful example if you would like to set different permastructures (conditional permalinks) for posts, products, pages and other custom post type items based on their categories, taxonomies or other conditions eg. post publication date, post author, post title (using $post object).

    Example 1. Dynamic/conditional custom permalinks for posts

    Below you can find a sample snippet that allows to change the default custom permalink assigned to the post according to their category. Please note that, the changed URIs will be applied only to the new posts and if you would like to apply the new default URIs to the old posts you should regenerate the permalinks using “Regenerate/reset” tool avaialable in Permalink Manager tools section.

    Basically, the posts filters the posts permalinks categorized as “Blog”, “Press Releases” or “Bottom line” items and all other posts permalinks will not be changed.

    function pm_filter_default_post_uris($default_uri, $native_slug, $post, $slug, $native_uri) {
    	// Do not change native permalinks or different post types
    	if($native_uri || $post->post_type != 'post') { return $default_uri; }
    	// Get the year when the post was published
    	$year = date("Y", strtotime($post->post_date));
    	// Get post author meta - it can be user_login, user_email, ID or other field:
    	// Reference: https://developer.wordpress.org/reference/functions/get_the_author_meta/
    	$author = get_the_author_meta('user_login', $post->post_author);
    	// 1A. Blog posts (you can use either category name, slug or its ID)
    	// Example URL: http://example.com/blog/lorem-ipsum/
    	if(in_category('blog', $post)) {
    		$default_uri =  "blog/{$slug}";
    	// 1B. Press releases
    	// Example URL: http://example.com/news-and-resources/press-releases/2016/dolor-sit-amet/
    	else if(in_category('press-releases', $post)) {
    		$default_uri =  "news-and-resources/press-releases/{$year}/{$slug}";
    	// 2. Posts added by John Doe
    	// Example URL: http://example.com/interviews/2018/john-doe/etiam-ullamcorper/
    	else if($author == 'john-doe') {
    		// Get the author full name
    		$author_name = sprintf("%s %s", 
    			get_the_author_meta('first_name', $post->post_author), 
    			get_the_author_meta('last_name', $post->post_author)
    		// Sanitize the author name, so it can be safely used inside URI
    		$author_safename = sanitize_title($author_name);
    		$default_uri =  "interviews/{$year}/{$author_safename}/{$slug}";
    	// 3A. Posts published in and after 2017
    	// Example URL: http://example.com/news-and-resources/malesuada-ultricies/
    	else if(intval($year) >= 2017) {
    		$default_uri =  "news-and-resources/{$slug}";
    	// 3B. Posts published before 2017
    	// Example URL: http://example.com/archive/2016/nulla-imperdiet/
    	else {
    		$default_uri =  "archive/{$year}/{$slug}";
    	return trim($default_uri, "/");
    add_filter('permalink_manager_filter_default_post_uri', 'pm_filter_default_post_uris', 10, 5);

    Example 2. Dynamic/conditional permalinks for custom post types items

    Analogous to the previous example, you can use different permalink formats/structures also for custom post types (eg. WooCommerce products) assigned to custom taxonomies.

    Below you can find a basic snippet showing how WooCommerce conditional permalinks can be set-up to change the default products web addresses according to their product categories (and eg. custom fields in the first case).

    function pm_filter_default_product_uris($default_uri, $native_slug, $post, $slug, $native_uri) {
    	// Do not change native permalinks or different post types
    	if($native_uri || $post->post_type != 'product') { return $default_uri; }
    	// Get WooCommerce product object & some meta data
    	$product = wc_get_product($post->ID);
    	$product_weight = $product->get_weight();
    	$product_sku = $product->get_sku();
    	// Get country of origin (set-up as a custom field) & prepare a sanitized string for URI
    	$product_coo = get_post_meta($post->ID, 'country', true);
    	// 1A. Products in "Toast" category
    	// Example URL: http://shop.com/bread/bruschetta/
    	else if(has_term('Toast', 'product_cat', $post)) {
    		$default_uri =  "bread/{$slug}";
    	// 1B. Products in "Honey" category
    	// Example URL: http://shop.com/eco-products/blossom-honey-jar/
    	else if(has_term('Honey', 'product_cat', $post)) {
    		$default_uri =  "eco-products/{$slug}";
    	// 2. Products in "Milk" category (you can use either category name, slug or its ID)
    	// Example URL: http://shop.com/dairy/netherlands/gouda-cheese/
    	if(has_term('Milk', 'product_cat', $post) && ($product_coo == 'Netherlands')) {
    		// Sanitize "country of origin"
    		$product_safe_coo = sanitize_title($product_coo);
    		$default_uri =  "dairy/{$product_safe_coo}/{$slug}";
    	// 3. Products heavier than 50kg with SKU inside permalink
    	// Example URL: http://shop.com/overgauge/abc-123-z/beer-keg/
    	else if($product_weight > 50) {
    		// Sanitize SKU
    		$product_safesku = sanitize_title($product_sku);
    		$default_uri =  sprintf("overgauge/{$product_safesku}/{$slug}");
    	// 4. Other products
    	// Example URL: http://shop.com/shop/liquorice-chocolate/
    	else {
    		$default_uri =  "shop/{$slug}";
    	return trim($default_uri, "/");
    add_filter('permalink_manager_filter_default_post_uri', 'pm_filter_default_product_uris', 10, 5);

    Other applications

    Two snippets presented above are only a basic examples. You can use permalink_manager_filter_default_post_uri filter also to dynamically change the default permalinks using as many conditions as you want, including eg. custom fields (meta keys), taxonomies, dates, post titles, authors and so on.