Need help with add_rewrite_rule

SOLUTION


I’ve gone with Update 3 and have got it working. What I had misunderstood, was that I believed that a rewrite rule would change designers/?designer=some-designer&bid=9 to /designers/some-designer. But it is of course the other way around.


I’ve been doing some research, but I don’t quite get how to use add_rewrite_rule().

This post is pretty close to what I need, but do I really need that much code?

My current link is this:
http://www.norwegianfashion.no/designers/?designer=Batlak-og-Selvig&bid=9
What I want is this
http://www.norwegianfashion.no/designers/Batlak-og-Selvig/

  1. How do I use add_rewrite_rule()?
  2. I need to pass the designer ID (bid). How can I do that when using re.write rules with out it showing in the URL?

Update 1

I found this which is a bit more explainatory. But it’s not working:

function add_rewrite_rules( $wp_rewrite ) 
{
    $new_rules = array(
        '('.$template_page_name.')/designers/(.*?)/?([0-9]{1,})/?$' => 'designers.php?designer=".
        $wp_rewrite->preg_index(1)."&bid='.
        $wp_rewrite->preg_index(2)
    );
       // Always add your rules to the top, to make sure your rules have priority
    $wp_rewrite->rules = $new_rules + $wp_rewrite->rules;
}
add_action('generate_rewrite_rules', 'add_rewrite_rules');

function query_vars($public_query_vars) {

    $public_query_vars[] = "designer";
    $public_query_vars[] = "bid";
    return $public_query_vars;
}
add_filter('query_vars', 'query_vars')

Update 2

Here is another solution I’ve tried, that Jan Fabry suggesten in another thread. I put this in my functions.php, but it has no effect.

add_action('generate_rewrite_rules', 'add_rewrite_rules');
add_filter('query_vars', 'query_vars');

/* Custom re-write rules for desigenrs
------------------------------------------------------------------------------*/
function add_rewrite_rules( $wp_rewrite ) 
{
    add_rewrite_rule('^designers/([^/]*)/([^/]*)$ /designers/?designer=$1&bid=$2 [L]', 'top');
    flush_rewrite_rules(false);
}


function query_vars($public_query_vars) {

    $public_query_vars[] = "designer";
    $public_query_vars[] = "bid";
    return $public_query_vars;
}

Update 3
I found a few examples here as well. But this is not working either 🙁

add_filter('rewrite_rules_array','wp_insertMyRewriteRules');
add_filter('query_vars','wp_insertMyRewriteQueryVars');
add_filter('init','flushRules');  

// Remember to flush_rules() when adding rules
function flushRules(){
    global $wp_rewrite;
    $wp_rewrite->flush_rules();
}

// Adding a new rule
function wp_insertMyRewriteRules($rules)
{
    $newrules = array();
    $newrules['(designers)/(\d*)$'] = 'index.php?pagename=designers&designer=$matches[1]';
    return $newrules + $rules;
}

// Adding the bid var so that WP recognizes it
function wp_insertMyRewriteQueryVars($vars)
{
    array_push($vars, 'designer');
    return $vars;
}

Update 4
I also tried this solution, but still no sucess.

add_filter('search_rewrite_rules', 'designers_createRewriteRules');

function designers_createRewriteRules($rewrite) {
global $wp_rewrite;

// add rewrite tokens
$designer_token = '%designers%';
$wp_rewrite->add_rewrite_tag($designer_token, '(.+)', 'designers=");

$bid_token = "%bid%';
$wp_rewrite->add_rewrite_tag($bid_token, '(.+)', 'bid=');

$keywords_structure = $wp_rewrite->root . "designers/$designer_token/$bid_token";
$keywords_rewrite = $wp_rewrite->generate_rewrite_rules($keywords_structure);

return ( $rewrite + $keywords_rewrite );

}

Update 5
It seems that my rewrite rules are not added.
Executing the follwoing afte radding new rules: print_r($wp_rewrite), does not include my new rewrite rules.

Update 6
I tested Jan’s solution. But URL does not change 🙁

/* Initiation script for NF template
------------------------------------------------------------------------------*/   
add_action('after_setup_theme','norwegianfashion_setup');

if ( ! function_exists( 'norwegianfashion_setup' ) ):

  function norwegianfashion_setup() { 
    /* Here I do a lot of stuff */

    // SEO friendly URL for desigenrs
    add_action( 'init', 'wpa5413_init' );
    add_filter( 'query_vars', 'wpa5413_query_vars' );
  }

endif;


/* Custom re-write rules for desigenrs
------------------------------------------------------------------------------*/
function wpa5413_init()
{
    // Remember to flush the rules once manually after you added this code!
    add_rewrite_rule(
        'designers/([^/]+)/?',
        'index.php?pagename=designers&designer=$matches[1]',
        'top' );
}

function wpa5413_query_vars( $query_vars )
{
    $query_vars[] = 'designer';
    return $query_vars;
}

I would really appreciate it if someone were able to help me solve this issue.

1

Some background on rewrite rules

The WP Rewrite system looks complicated, but in fact it’s not that hard to understand how it works. It is used to parse pretty URLs (/designers/) as if they were non-pretty (/index.php?pagename=designers). The basic idea is that you have a list of rewrite rules, regular expressions that can match the incoming URLs, and rewrite them into other URLs, mostly in the form of index.php with some query variables. These variables are set to parts of the regular expression that matched the URL. The rules are evaluated top to bottom, so if two rules might match, the first one “wins”.

These rewrite rules are stored in the database by calling flush_rewrite_rules(), but because generating and writing these rules is an expensive operation, you should not do it on every init. Instead, you only do it when you change the rules: probably on plugin activation (using register_activation_hook()), or manually by visiting the Permalinks settings page (because it’s hard to do this automatically on theme activation). If you don’t flush the rules, calling add_rewrite_rule() has no effect. It is no problem to call add_rewrite_rule() on every init, even after you flushed the rules.

There are many ways to add your rewrite rules, and if you only want to add one rule, you can do this with add_rewrite_rule(). Because the rules for posts, pages, taxonomies, … result in multiple rules, they are created with high level functions like add_rewrite_tag(), add_permastruct(), … But these are not needed for the simple case we have here.

There are also some filters, so you can also add your rules using the generate_rewrite_rules or generate_rewrite_rules_array hooks. These give you very detailed control over the complete rules, but again, add_rewrite_rule() is enough if you just want to add one rule.

I created a little plugin that allows you to analyze the current rewrite rules, and (of course) I recommend you use it when you try to change anything.

Getting your extra info from the URL

Let’s build on the example you gave. You have the designer brand Batlak og Selvig, with the numberic ID (bid) 9. You don’t want this ID in the URL, but only the name. We call the URL-friendly version of the name (without spaces or special characters) the slug: Batlak-og-Selvig.

In general, the URL should contain all info to identify the content you want to show on the page. You can use cookies or POST requests to store the actual designer ID you want to display, but this will break down if anyone shares the link, by sending it via mail, Facebook, Twitter, or just reading it out loud over the phone. If you don’t want to use the numeric ID in your URL, you should make sure the slug is unique: then you first do a lookup of the ID based on the slug, and continue as you did before.

So what we have learned now is that we want to add a rewrite rule for the pattern /designers/ followed by anything up to the next /, and save that “anything” in the designer_slug query variable. In your case I believe you also want to use the page with the slug designers as the general page content, so we’ll tell WordPress that too.

add_action( 'init', 'wpa5413_init' );
function wpa5413_init()
{
    // Remember to flush the rules once manually after you added this code!
    add_rewrite_rule(
        // The regex to match the incoming URL
        'designers/([^/]+)/?',
        // The resulting internal URL: `index.php` because we still use WordPress
        // `pagename` because we use this WordPress page
        // `designer_slug` because we assign the first captured regex part to this variable
        'index.php?pagename=designers&designer_slug=$matches[1]',
        // This is a rather specific URL, so we add it to the top of the list
        // Otherwise, the "catch-all" rules at the bottom (for pages and attachments) will "win"
        'top' );
}

The only thing left to do is to tell WordPress to also keep the designer_slug in the query variables, so you can access it later. WordPress uses a whitelist of allowed query variables, to prevent people from modifying every parameter of the posts query via the URL. Everything not on this list is ignored. Just hook into the query_vars hook and add your extra variable:

add_filter( 'query_vars', 'wpa5413_query_vars' );
function wpa5413_query_vars( $query_vars )
{
    $query_vars[] = 'designer_slug';
    return $query_vars;
}

As you can see, this answer is basically the same as my previous answer, but without the rule flushing in the code (so you can use it in your theme’s functions.php instead of a plugin), and with a bit more certainty and explanation because I learned a lot about rewrite rules since (and because of) answering your first question.

Chat room discussion

We cleared up final issues for this question in the chat room. In the end, Steven used the generate_rewrite_rules hook, because the add_rewrite_rule() function did not seem to work for him.

Leave a Comment