Desired permalink structure using region and category

Hello,

I enabled generate regions from location in Geolocation settings. It’s creating the region successfully where a new listing creates the State > County > City hierarchy in regions.

I’m trying to figure out how to structure my permalinks using regions and categories to look like this:

mysite.com/state/city/category/listing-slug

Is there a way to do this? I don’t want the county…just state and city.

Hi,

You can change the permalink structure using this documentation: How to customize URL structure - HivePress Help Center. All other settings will require custom implementation, .e.g Using Post ID in Listing URL

​I hope this is helpful to you

Thanks, Andrii.

Sorry I wasn’t more clear. I did follow the how to and currently have mysite.com/state/listing-slug instead of the default mysite.com/listing/listing-slug, however, what I am trying to figure out is how to pull in the region and category values.

The %category% structure tag doesn’t pull in the listing category. For example, I tried to use /state/%category%/%postname%/ as a custom structure and that does not work.

I can change listing to state, but how do I pull in the city from the listing’s region and category from the listing’s category?

Hi,

Such settings are beyond the available options and can only be added with the help of custom code, as WordPress does not have such permalink settings for custom taxonomy. You can find the example at this link: php - How to add custom taxonomy in custom post type permalink? - Stack Overflow, listing is hp_listing post type, regions is hp_listing_region taxonomy.

I hope it helps

Thanks for the example, andrii. I played around with it and created a snippet. Although I was able to get the custom permalink structure I wanted when adding a new listing, unfortunately, the link goes to a nothing found page.

I was hoping to include the city and category to help with local SEO, but not really sure if it makes a difference or not. I just notice sites like Tripadvisor and YP do this, but Yelp does not.

If anyone else has an interest in something like this let me know and I’ll post the snippet. Otherwise, I’ll probably settle for changing the base from listing to my state and call it good. I don’t want this to hold up my launch, but I also don’t want to build out a bunch of links and try to change them later if this is possible.

Hello @kja

Did you save your permalinks (under Settings) ?

Yes, share your snippet, by all means

Thanks @cotasson

Yes, I save the permalinks to flush the rewrite rules.

OK, I think I got it working! (with the help of Claude) Please forgive my noobiness.

NOTE: I could not get this to use the listing base that is set in Settings > Permalinks properly so I am hard coding it in this snippet. Change ‘listing-base’ to whatever you want your base to be. There are four places to change this.

// Modify the permalink structure for hp_listing to include region (city) and category
function custom_hp_listing_permalink($permalink, $post) {
    // Ensure we are modifying hp_listing post type
    if ($post->post_type === 'hp_listing') {
        // Get the category terms
        $category_terms = get_the_terms($post->ID, 'hp_listing_category');
        // Get the region terms
        $region_terms = get_the_terms($post->ID, 'hp_listing_region');
        
        // Check if both taxonomies have terms
        if ($category_terms && !is_wp_error($category_terms) && 
            $region_terms && !is_wp_error($region_terms)) {
            
            $category_slug = $category_terms[0]->slug; // Get first category
            
            // Get the lowest level region (city)
            $city_term = null;
            foreach ($region_terms as $term) {
                // If term has no children, it's the lowest level
                $children = get_term_children($term->term_id, 'hp_listing_region');
                if (empty($children)) {
                    $city_term = $term;
                    break;
                }
            }
            
            // If we found a city, include it in the URL
            if ($city_term) {
                return home_url("/listing-base/{$city_term->slug}/$category_slug/$post->post_name/");
            } else {
                // Fallback to just category if no city is found
                return home_url("/listing-base/$category_slug/$post->post_name/");
            }
        }
    }
    return $permalink;
}
add_filter('post_type_link', 'custom_hp_listing_permalink', 10, 2);

// Add custom rewrite rule to match the new URL structure
function custom_hp_listing_rewrite_rules() {
    add_rewrite_rule(
        '^listing-base/([^/]+)/([^/]+)/([^/]+)/?$',
        'index.php?hp_listing=$matches[3]&hp_listing_category=$matches[2]&hp_listing_region=$matches[1]',
        'top'
    );
    
    // Add fallback rule for listings without city
    add_rewrite_rule(
        '^listing-base/([^/]+)/([^/]+)/?$',
        'index.php?hp_listing=$matches[2]&hp_listing_category=$matches[1]',
        'top'
    );
}
add_action('init', 'custom_hp_listing_rewrite_rules');

// Flush rewrite rules when activating the theme or plugin
function flush_custom_hp_listing_rewrites() {
    custom_hp_listing_rewrite_rules();
    flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'flush_custom_hp_listing_rewrites');
register_deactivation_hook(__FILE__, 'flush_rewrite_rules'); // Clean up on deactivation

If you don’t use regions, this snippet will just add the listing category to the permalink:

NOTE: Again, I could not get this to use the listing base that is set in Settings > Permalinks properly so I am hard coding it in this snippet. Change ‘listing-base’ to whatever you want your base to be. There are two places to change this.

// Modify the permalink structure for hp_listing to include category
function custom_hp_listing_permalink($permalink, $post) {
    // Ensure we are modifying hp_listing post type
    if ($post->post_type === 'hp_listing') {
        $terms = get_the_terms($post->ID, 'hp_listing_category');
        if ($terms && !is_wp_error($terms)) {
            $category_slug = $terms[0]->slug; // Get first category
            return home_url("/listing-base/$category_slug/$post->post_name/");
        }
    }
    return $permalink;
}
add_filter('post_type_link', 'custom_hp_listing_permalink', 10, 2);

// Add custom rewrite rule to match the new URL structure
function custom_hp_listing_rewrite_rules() {
    add_rewrite_rule(
        '^listing-base/([^/]+)/([^/]+)/?$',
        'index.php?hp_listing=$matches[2]&hp_listing_category=$matches[1]',
        'top'
    );
}
add_action('init', 'custom_hp_listing_rewrite_rules');

// Flush rewrite rules when activating the theme or plugin
function flush_custom_hp_listing_rewrites() {
    custom_hp_listing_rewrite_rules();
    flush_rewrite_rules();
}
register_activation_hook(__FILE__, 'flush_custom_hp_listing_rewrites');
register_deactivation_hook(__FILE__, 'flush_rewrite_rules'); // Clean up on deactivation

I am open to suggestions if there is a better way or if there is unnecessary code. I’m still learning!

3 Likes

Thanks for sharing!

I cleaned up the snippet to remove unnecessary code.

Key changes made:

  1. Removed the city-finding loop since WordPress handles hierarchical terms automatically
  2. Removed the fallback rules since both region and category will always be present
  3. Removed all the rewrite rules flushing code since WordPress handles this when saving permalinks

Here is the revised snippet to include the listing region and category for:
mysite.com/listing-base/region/category/listing-slug

<?php
// Modify the permalink structure for hp_listing to include region and category
function custom_hp_listing_permalink($permalink, $post) {
    // Ensure we are modifying hp_listing post type
    if ($post->post_type === 'hp_listing') {
        // Get the category and region terms
        $category_terms = get_the_terms($post->ID, 'hp_listing_category');
        $region_terms = get_the_terms($post->ID, 'hp_listing_region');
        
        // Check if both taxonomies have terms
        if ($category_terms && !is_wp_error($category_terms) && 
            $region_terms && !is_wp_error($region_terms)) {
            
            // Get first category and region (WordPress handles hierarchy automatically)
            $category_slug = $category_terms[0]->slug;
            $region_slug = $region_terms[0]->slug;
            
            return home_url("/listing-base/{$region_slug}/{$category_slug}/{$post->post_name}/");
        }
    }
    return $permalink;
}
add_filter('post_type_link', 'custom_hp_listing_permalink', 10, 2);

// Add custom rewrite rule to match the URL structure
function custom_hp_listing_rewrite_rules() {
    add_rewrite_rule(
        '^listing-base/([^/]+)/([^/]+)/([^/]+)/?$',
        'index.php?hp_listing=$matches[3]&hp_listing_category=$matches[2]&hp_listing_region=$matches[1]',
        'top'
    );
}
add_action('init', 'custom_hp_listing_rewrite_rules');

Here is the revised snippet to include the listing category for:
mysite.com/listing-base/category/listing-slug

<?php
// Modify the permalink structure for hp_listing to include category
function custom_hp_listing_permalink($permalink, $post) {
    // Ensure we are modifying hp_listing post type
    if ($post->post_type === 'hp_listing') {
        $terms = get_the_terms($post->ID, 'hp_listing_category');
        if ($terms && !is_wp_error($terms)) {
            $category_slug = $terms[0]->slug;
            return home_url("/listing-base/$category_slug/$post->post_name/");
        }
    }
    return $permalink;
}
add_filter('post_type_link', 'custom_hp_listing_permalink', 10, 2);

// Add custom rewrite rule to match the URL structure
function custom_hp_listing_rewrite_rules() {
    add_rewrite_rule(
        '^listing-base/([^/]+)/([^/]+)/?$',
        'index.php?hp_listing=$matches[2]&hp_listing_category=$matches[1]',
        'top'
    );
}
add_action('init', 'custom_hp_listing_rewrite_rules');

Just change ‘listing-base’ to whatever you want your base to be, then go to Settings > Permalinks and click save to flush the rewrite rules.

1 Like