32

Is it possible to redeclare/change the slug of a plugin's existing custom post type (without simply editing the plugin)?

That is, if Plugin X creates a custom post type with the slug /uncookedtoast/, is it possible to add a filter to functions.php (or something similar) that changes the slug to /bread/?

1
  • I just solved this same issue by hooking into init with a priority of 20 and then calling register_post_type again for the same post type. Anyone know if this is going to cause me any trouble down the road?
    – Dustin
    Apr 12, 2013 at 2:48

4 Answers 4

31

Yes, this is possible, but if the plugin is creating a custom post type using the rewrite => array('slug' => 'post_type') parameter, then it's not likely that you're going to be able to replace the slug.

Whenever custom post types are created, URL rewrite rules are written to the database. Depending on which action triggers the creation of the custom post type (such as the init action), WordPress will flush the rewrite rules and restore the custom post type's slugs regardless of what changes you attempt to make.

That said, you can provide custom slugs for the custom post types. The following example assumes that you have a custom post type of movies and that you're attempting to change the /movies/ slug to /films/.

To be complete, here's the basic function used to define the movies custom post type. The plugin that you're referencing should be doing something like this:

function movies_register_post_type() {

    register_post_type(
        'movies',
        array(
            'labels' => array(
                'name' => __('Movies'),
                'singular_name' => __('Movie')
            ),
            'public' => true,
            'has_archive' => true,
            'rewrite' => array(
                'slug' => 'movies'
            )
        )
    );

} // end example_register_post_type
add_action('init', 'movies_register_post_type');

You can modify the options table by providing your own custom rules based on the existing post type.

Basically, we'll do this:

  • Take the existing set of rules and then write our own with our own custom slugs
  • Give the new rule a higher priority than the custom post type's slug

Here's how you can do this:

function add_custom_rewrite_rule() {

    // First, try to load up the rewrite rules. We do this just in case
    // the default permalink structure is being used.
    if( ($current_rules = get_option('rewrite_rules')) ) {

        // Next, iterate through each custom rule adding a new rule
        // that replaces 'movies' with 'films' and give it a higher
        // priority than the existing rule.
        foreach($current_rules as $key => $val) {
            if(strpos($key, 'movies') !== false) {
                add_rewrite_rule(str_ireplace('movies', 'films', $key), $val, 'top');   
            } // end if
        } // end foreach

    } // end if/else

    // ...and we flush the rules
    flush_rewrite_rules();

} // end add_custom_rewrite_rule
add_action('init', 'add_custom_rewrite_rule');

Now, you'll have two ways to access your movies:

  • /movies/Back-To-The-Future
  • /films/Back-To-The-Future

Note that I don't recommend hooking the add_custom_rewrite_rule into the init action as it will fire too frequently. This is just an example. A better place to apply the function would be on theme activation, plugin activate, perhaps the save_post action, etc. Depending on what you need to do, you may only need to fire it once or just a few times.

At this point, you may want to consider updating the permalinks for the custom post type to use the '/movies/ slug. For example, if you navigate to /films/, you will see a listing of all your movies but hovering over the title will reveal that the /movies/ slug is still being used.

To go one step further, you could technically instate a 301 redirect to catch all links to /movies/ to redirect to their /films/ counterpart, but this all depends on what you're attempting to do.

3
  • Worked beautifully! Could you also point me in the right direction for updating the permalinks?
    – Edbury
    Feb 13, 2012 at 20:23
  • 1
    Depending on your theme, you can do this at the template level. When you see the call to 'the_title()', you can replace that with $title = get_the_title(), then string replace '/movies/' in $title with '/films/'.
    – Tom
    Feb 14, 2012 at 13:16
  • What if the slug you want to change to is dynamically created by a meta value of the current post? I need to be able to use $post in the add_custom_rewrite_rule() function but it's throwing errors in WP Admin saying 'Trying to get property of non-object'. How can I get $post to get the meta value?
    – Lee
    Jan 17, 2018 at 16:15
15

This code worked well for my child theme. Needed to change "program" slug to "place".

/*
CHANGE SLUGS OF CUSTOM POST TYPES
*/

function change_post_types_slug( $args, $post_type ) {

   /*item post type slug*/   
   if ( 'program' === $post_type ) {
      $args['rewrite']['slug'] = 'place';
   }

   return $args;
}
add_filter( 'register_post_type_args', 'change_post_types_slug', 10, 2 );

/*
CHANGE SLUGS OF TAXONOMIES, slugs used for archive pages
*/

function change_taxonomies_slug( $args, $taxonomy ) {

   /*item category*/
   if ( 'program-category' === $taxonomy ) {
      $args['rewrite']['slug'] = 'locations';
   }

   return $args;
}
add_filter( 'register_taxonomy_args', 'change_taxonomies_slug', 10, 2 );
3
  • It works perfectly, I have also used it for the tags adding the relative rule, it is necessary to update the permalinks by executing a save from the settings page > permalink. Thank you! Oct 19, 2018 at 18:17
  • Works perfectly to change other args too, I needed to set $args['has_archive'] = false and this worked. Feb 8, 2019 at 16:35
  • What if I wanted to change the post permalink instead and not the cpt slug, like: themename/projects/**slug-of-post** ??
    – Krys
    Oct 15, 2020 at 9:05
0

In my case there was a plugin (needed for the theme to work) registering a custom post type and I wanted to change some things on it, the slug among others.

So, what I've done is to re-register (kind of overwrite of the plugin's registering function) the custom post type by adding the following in my child theme's functions.php file like so:

function my-theme-name_post_type_my-custom-post-type-name()
{
   $labels = array(
      ...
   );

   $args = array(
      ...

      'rewrite' => array(
         'slug' => __('the-slug-of-your-choise', 'the-textdomain-of-your-choise-for-translation-if-needed'),
      ),
   );
   register_post_type('the-custom-post-type-name-registered-by-the-plugin', $args);
}
add_action('init', 'my-theme-name_post_type_my-custom-post-type-name');

As you can see, you can change the slug by simply adding the following to the $args array:

'rewrite' => array(
    'slug' => __('the-slug-of-your-choise', 'the-textdomain-of-your-choise-for-translation-if-needed'),
)

More infos on this at: https://developer.wordpress.org/reference/functions/register_post_type/

VERY IMPORTANT

Remember to refresh you permalinks in order the changes to be applied and so you can access the involved posts. Go to the permalinks settings and simply press "Save"..then it should work!😀

0
function change_portfolio_slug() {
    $args = get_post_type_object( 'portfolio' );
    if ( ! empty( $args ) ) {
        $args->rewrite['slug'] = 'project';
        register_post_type( 'portfolio', $args );
    }
}
add_action( 'init', 'change_portfolio_slug', 20 );

In my case, plugin declares custom post type "portfolio" with 'rewrite' => array( 'slug' => 'portfolio'), with a hook in the 'init' action add_action( 'init', 'some_plugin_custom_posts_init' );.

The above snippet, placed in a theme's functions.php file, defines a new function change_portfolio_slug() that modifies the rewrite argument for the portfolio post type, changing its slug to "project". We then use the register_post_type() function to register the post type with the modified arguments.

The add_action() function hooks the change_portfolio_slug() function to the init action, so that it is executed when WordPress initializes.

In the last line of the snippet add_action( 'init', 'change_portfolio_slug', 10 );, the number 10 is the priority of the action hook. If you want to change the priority, you could change the 10 to a different number. In my case, the working priority was 20.

Note that you may need to refresh your permalinks after making this change to ensure that the new URL structure is applied correctly. You can do this by going to Settings > Permalinks in your WordPress admin dashboard and clicking the "Save Changes" button.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.