I have created a custom post type called "events" and have also created a custom taxonomy which only shows up on this specific post type through which the admin must select an "event category" (terms).

Currently on the public site when the event detail page is requested it looks like this:


Additionally, on the public side a web browser can access just a specific event category to view all associating events where the url looks like this:


Both of the above situations currently works fine.

The problem I am having is that I would like to customize the URL slug for the event details page as follows:

Instead of having the event detail page slug being built from the entered title I would like for it to include the taxonomy term and a specific custom field. In end effect I would like for it to look like this:

  • <taxonomy-term> is the event category (the way I programed the metabox the admin is only able to select a single term).
  • <custom-field> is a specific custom field the user is required to enter before the event post can be saved.

Does anyone know how I might go about doing this correctly? All my experiments have led nowhere.

  • How about updating the post slug at save_post hook? Or am I way off?
    – Bainternet
    Commented Jan 22, 2011 at 3:07

1 Answer 1


Well if I had thought about how much work with was going to be before writing it, you wouldn't be able to get me points right now. :) Needless to say, it's rather involved (though I don't think it really should have to be, but it is what it is.)

First off, you can't (easily) do it like you wanted; the problem is how to differentiate between these two URLs:

  • Event Type = "Day Time", Event Title = "For a Change"

  • Event Type = "Day", Event Title = "Time For a Change"

Both would have a portion of their URLs looking like this and how could we tell which is which?


So, I modified your pattern to use tildes (~) as separators and that works fine:


Anyway, I wrote a plugin that does what you need. It does the following things:

  1. It sets the rewrite rule in the the plugin activation hook using $wp_rewrite->add_rule().

  2. It applys the pattern to create actual event post links using the 'post_type_link' hook, and

  3. It simplifes editing of the URL slug by using the 'get_sample_permalink_html' hook (I didn't try to show the full URL because it would have been too tricky for something I'm writing for free. :)

So here's what the edit screen looks like:

Screenshot of Event Edit Screen on WordPress
(source: mikeschinkel.com)

Which produces this view on the front end:

Example of Custom URLs in WordPress
(source: mikeschinkel.com)

And here ya go with the plugin code (let me know if you have questions, but not toooo many. ;-):

Plugin Name: SEO URLs for Events
if (!class_exists('SeoUrlsForEvents')) {
  class SeoUrlsForEvents {
    static function on_load() {
      add_action('init', array(__CLASS__,'init'));
      add_filter('post_type_link', array(__CLASS__,'post_type_link'),10,4);
    static function get_sample_permalink_html($sample, $post_id, $new_title, $new_slug) {
      $post = get_post($post_id);
      if ($post->post_type=='event') {
        $post_slug = self::make_post_slug($post);
        if ($post_slug) {
          $sample = str_replace('<strong>Permalink:</strong>','<strong>Permalink Slug:</strong>',$sample);
          $sample = preg_replace('#(<span id="sample-permalink">).+?(<span id="editable-post-name" title="Click to edit this part of the permalink">).+?</span>/</span>#Us',"$1$2{$post->post_name}</span></span>",$sample);
          $permalink = get_post_permalink($id);
          $sample = preg_replace("#(<span id='view-post-btn'><a href=').+?(' class='button' target='_blank'>View Post</a></span>)#","$1{$permalink}$2",$sample);
      return $sample;
    static function make_post_slug($post) {
      $post_slug = false;
      $post = (object)$post;
      if ($post->post_type=='event') {
        $when = get_post_meta($post->ID,'when',true);
        if (!empty($when)) { // If we don't have a when, use default
          $types = wp_get_object_terms($post->ID,'event-type');
          if (count($types)) // If we don't have an event-type, use default else use the first one
            $post_slug = "{$types[0]->slug}~{$post->post_name}~{$when}/";
      return $post_slug;
    static function post_type_link($post_link, $post, $leavename, $sample) {
      if ($post->post_type=='event' && $sample) {
        $post_link = '%event%';
      } else {
        $post_slug = self::make_post_slug($post);
        if ($post_slug) {
          $post_link = get_bloginfo('wpurl') . '/events/' . $post_slug;
      return $post_link;
    static function activate() {
      global $wp_rewrite;
      $wp_rewrite->add_rule('events/([^~]+)~([^~]+)~([^/]+)','index.php?post_type=event&taxonomy=event-type&term=$matches[1]&name=$matches[2]&meta_key=when&meta_value=$matches[3]', 'top');
    static function init() {
          'label'           => 'Events',
          'public'          => true,
          'rewrite'         => array('slug' => 'events'),
          'hierarchical'    => false,
          'supports'        => array('title','editor','custom-fields'),
      register_taxonomy('event-type', 'event', array(
        'hierarchical'    => false,
        'label'           => 'Event Types',
        'rewrite'         => array('slug' => 'event-types' ),
  • Mike - for some reason I noticed I never commented on your response thanking you for your response! So... thank you :-) I am curious if you might know if the new version of wp simplifies this to any extent? Commented Jun 28, 2012 at 6:04
  • @NetConstructor.com I've taken a sabattical from WordPress Answers for a while while my business have been consuming me. I'm back, but still with very limited time. AFAIK, nothing new in this area in 3.4. Commented Jun 29, 2012 at 2:50

Your Answer

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

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