Ask your WordPress questions! Pay money and get answers fast! Comodo Trusted Site Seal
Official PayPal Seal

Need WP Rewrite Rules working code example WordPress

  • REFUNDED

I'm rewriting my question and increasing the total award to $50, for which I would like the following functional code:

A rewrite rule that will parse the requested page URL and do the following with it:

given URL format: site_url()/post-name/some/additional/bunch/of/stuff/

The keyword in this case would be "stuff" and should be assigned to a constant 'KEYWORD'.

The actual post to rewrite to, would be site_url()/post-name/ but it is vital that the URL be rewritten, NOT redirected. The address bar must reflect the full requested URL, complete with any custom query string.

So in the address bar I would see something possibly like:

www.domain.com/blog/post-name/some/additional/bunch/of/stuff/?var1=red&var2=blue

but internally WP would be pulling content from the DB associated with 'post-name'.

Oh forgot one important point, it actually should work with whatever the current permalink rules are, so really the url format would be:

site_url()/%permalink_structure%/some/additional/bunch/of/stuff/

Thanks everyone!

EDIT:

This article seems to be talking about something similar to what I need:

http://betterwp.net/98-wordpress-create-fake-pages/

Answers (5)

2011-10-10

Dylan Kuhn answers:

The parse_query action is the earliest action I know of after is_archive is set for the global query. pre_get_posts is a popular one too, still early enough to redirect if you need to. At init, since the query hasn't been parsed yet, is_archive() will always return false.


Jonathan van Clute comments:

Thanks, I wish it were that easy. I have a seeming paradox. I need to manipulate the SERVER_URI before WP has done all its internal rewriting and such, and it looks like that means I can't do my manipulations any later than 'init'. But I also need to detect if the visitor is on an archive page BEFORE I finish my manipulations, and is_archive can't work until after init. Catch-22.

Any other ideas?

2011-10-10

Bob Keen answers:

Jonathan,

You can check if a URI is associated with a specific post/page as early as "setup_theme".

The key is to use the WP function "url_to_postid()", which will check a URL and return a post->ID if it matches a permalink.

More on this here: http://codex.wordpress.org/Function_Reference/url_to_postid

If the URL doesn't resolve to a page/post, you'll get a 0 back.

If you get a valid post->ID back (non-zero), it will be a page/post.

If you get a 0, however, you'll want to begin a short process of elimination, such as URI which contain ".php" (i.e. wp-login.php) as well as any specialties on your, and arrive at the determination as to whether you're dealing with an is_archive() or not.

Best,
Bob


Jonathan van Clute comments:

Thanks Bob, that's basically what I'm already doing. The problem is my plugin is rather unusual, and does a whole bunch of custom rewriting so that I can insert any keyword I want at the end of the URL and various things can then be done with it.

So if I have an initial permalink that looks like this:

www.domain.com/blog/my-post/

it might look like this after adding the keyword "dogs":

www.domain.com/blog/my-post/dogs/

Of course, there is no such page no matter what, as no DB entry is ever created. So I am already looping through and parsing down the requested URI piece by piece, checking url_to_postid along the way to get to the base or "real" post, and then changing SERVER_URI to be that URI.

All that works fine... but the big issue is categories or archives. For instance a category URL might look like this:

www.domain.com/blog/category/

But how can I tell if this is a category request, or if "category" is the keyword chosen for the blog root page? I must positively identify /category/ as a category page (using is_archive() ideally) in order to not treat it as a keyword.

So I can't go off of url_to_postid values, because in this situation both the keyword page AND a category page will always return 0 for the postid anyway.

Hope you see my dilemma and perhaps have some more ideas.

Thanks again!


Bob Keen comments:

I don't know how you're doing you rewrites, but you can try to hook into "sanitize_comment_cookie" - which is before $wp_rewrite has been initiated. You may get some ideas moving from there, based on the data you're getting.

You can also look for clues through the categories in the taxonomy table and see if you get hits on relevant portions of the URI that would indicate that you're dealing with a parent or child category.

2011-10-10

John Cotton answers:

I would have thought that you are on very dodgy ground changing the SERVER_URI value. Clearly I don't know your plugin, but I can't think of any reason why you'd want to do it other than to fool WordPress into thinking that a different page had been requested.

If that's what you're trying to do, then you are doing it the wrong way.

You should being adding custom rewrite rules (using the 'rewrite_rules_array' action), probably creating your own query vars (using the 'query_vars' action) in order to neatly pass through to the destination page or template redirect the extra info you need to define the page switch and then, finally, using the 'template_redirect' action (if necessary) as the correct place to redirect WordPress to the desired end point.

I say 'if necessary' since you may well be able to create a rewrite rule that takes you there directly.

Your problem of "how can I tell if it's a category or a keyword" is the age old one of uniqueness and, if you don't allow yourself unque URLs you're on a hiding to nothing.

Why not change your url to www.domain.com/blog/my-post/keywords/dogs/ where keywords (or whatever) is reliably unique allowing you to construct a decent regex e.g.

www.domain.com/(.*)/keywords/(.*)

JC


Jonathan van Clute comments:

You're exactly right that I'm trying to "fool" WP into loading a different page than what's seen in the URL. It was a far simpler approach than getting into rewrite rules - at least until archives came into play.

I don't want to designate a "delimiter" word in my URLs, it's important to me that they simply follow the format of /post-name/some/possible/keywords/ where "keywords" is the desired target keyword, since it is the last item in the url.

I'm still quite new (a few months) to developing on WP, so all the various hooks, filters, actions, etc. tend to throw me for a loop. Could you possibly supply some sample code for creating a rewrite to accomplish the above? It sounds like this would give me more flexibility in terms of what point in the logic to execute things, which would enable me to use more conditionals such as is_archive and others.

I'm going to increase the award for this question and update it with my additional requirements. Thanks!


John Cotton comments:

This the basic structure:


function template_redirect() {
if( get_query_var( 'something_special' ) == 1 ) {
// Do whatever you want!
// Test get_query_var( 'lead' ) and get_query_var( 'follow' ) to find your matches and then off you go
exit();
}
}
add_action('template_redirect','eg_template_redirect');

function eg_add_rewrite_rules( $rules ) {
$newrules = array();

$newrules['^(.*)/marker/(.*)$'] = 'index.php?something_special=1&lead=$matches[1]&follow=$matches[2]';

return $newrules + $rules;
}
add_action('rewrite_rules_array', 'eg_add_rewrite_rules');


function eg_add_query_vars( $vars ) {
array_push($vars, 'something_special', 'lead', 'follow');

return $vars;
}
add_action('query_vars', 'eg_add_query_vars');


but it's crucial that you have some marker in the url otherwise it's impossible to write a rule that won't match every (and thus stop WordPress doing it's work).

What reason don't you want a marker word - SEO?


Jonathan van Clute comments:

Yes, SEO is a primary reason, but also so that datafeed (csv data usually) driven sites can be easily created simply by adding the product keyword one wishes to pull a page for, right in the URL. For instance:

www.domain.com/blog/some-post/sports/athletic-wear/golf-shoes/

This would match to my datafeed file for the term "golf shoes" and all the other stuff is merely there to give the appearance of a careful heirarchy. I already have this code working provided I feed it a keyword. In this way I can quickly construct URLs for all manner of things without having to do anything more than add something to the URL.

Looking over the code you posted, I have to admit to not following it really at all. It looks as if it parses the URL for something (the marker in your example) and then rewrites behind the scenes to some query string variables? Is that right?


John Cotton comments:

The search engines aren't going to fuss about an extra entry in here, trust me:

www.domain.com/blog/some-post/--/sports/athletic-wear/golf-shoes/

As you can see, it can be whatever you want, it just needs to be unique.

The code works from the bottom up:

eg_add_query_vars: WordPress needs to know about your new query variables so that it doesn't return a 404 (it will do that in the header even if you successfully return content!)

eg_add_rewrite_rules: We need to add some rules for your special urls. These get added to the top of the list of rules that WP checks so it's important that there is no chance of them matching an existing rule. So, for your standard /%category%/%postname%/ permalink, the rule is this:

$newrules["(.+?)/([^/]+)$"]= "index.php?category_name=$matches[1]&name=$matches[2]";

Unfortunately, that would also match your sample link which is why you need to add a marker....

$newrules["(.+?)/([^/]+)/k/([^/]+)$"]= "index.php?category_name=$matches[1]&name=$matches[2]&keywords=$matches[3]";

Can adjust your rewrites based on the current permalink? Absolutely, the global $wp_rewrite->permalink_structure tells you want the shape is and you can then adjust accordingly.

eg_template_redirect: (beware, the function is my first response is wrongly name!!) This is your chance to tell WordPress to go somewhere other than where it expects. So, for your CSV output, you could have this:


function eg_template_redirect() {


if( !empty( get_query_var('keywords')) {
include('template-csv-output.php');
exit();
}
}
add_action('template_redirect', 'eg_template_redirect');


which would send WP to a file called template-csv-output.php where you do whatever it is you do to output CSV.

Does that help?



Jonathan van Clute comments:

I'm afraid the requirement of not having a delimiter is absolute. It's not really about the search engines, it's simply how I need it to function.

Could perhaps the post name itself be a delimiter?

Also redirecting WP to a file to manage certain output is not acceptable. The URL format must be as I outlined, and not redirect to something like 'template-csv-output.php' in order to process a csv.

Thanks anyway, but unless you can think of a way to do exactly as I need, I'm afraid this won't do.


John Cotton comments:

<blockquote>It's not really about the search engines, it's simply how I need it to function.
Could perhaps the post name itself be a delimiter?
</blockquote>
You can try.....I'm not clear about why you don't want something else in the URL. If I was I might be able to suggest something.

For example if "sports" is always there, then that will do. Equally, if it's sports or games or toys (ie a defined list) then that could be made to work too.

<blockquote>Also redirecting WP to a file to manage certain output is not acceptable.</blockquote>
You've misunderstood. The URL stays the same, it's WordPress that redirects itself.

It always does that, redirecting to page.php, or single.php or archive.php depending on the page requested and the files in your theme. In this case, you can have it nicely sent to a clean PHP file to output the feed with no header or footer calls.


Jonathan van Clute comments:

OK so it's not a redirection it's a rewrite. When I saw "redirect" in the code, I assumed it was a real redirect.

What I'm doing is converting an existing non-WP application, to a WP plugin. This means that It must function the same way the old one did, I can't suddenly change the basic way the URLs are constructed by requiring a delimiter. There will never be anything that is guaranteed to always be present in the URL.

If it helps, here is the htaccess rewrite rules I've been using for years (on the non_WP version):

RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^.*$ - [S=44]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^.*$ - [S=44]
RewriteRule ^(.*)$ index.php?k=$1&%{QUERY_STRING}

So this is the behavior I need to recreate within WP.


Jonathan van Clute comments:

This article seems to be talking about something similar to what I need:

http://betterwp.net/98-wordpress-create-fake-pages/


John Cotton comments:

<blockquote>This article seems to be talking about something similar to what I need:</blockquote>

It does...because it's talking about doing exactly what I have described to you ;)

RewriteRule ^(.*)$ index.php?k=$1&%{QUERY_STRING}

The above line is what's making your previous code work and there's nothing to stop you having an identical rule in your custom rewrites.

You can then process the url in template redirect as I described.

However, if you do <strong>IT WILL PICK UP JUST ABOUT EVERY SINGLE URL</strong>.

So you will have to do all the legwork that WordPress does for you already because none of the wp_query settings will be set (since your rewrite rule will have come in before any of the others).

To quote Luis:

<blockquote>you are trying to solve the problem the hardest possible way.</blockquote>

Is your list of keywords (or at least the first one of them at the "sports" position") really that infinite that you can't predict it? I can't understand how you can have a page where <em>anything</em> can be passed in...

Even it's 50, or 100 or 250 long, you can still create restricted, identifiable regexs.




Jonathan van Clute comments:

The "sports" example I gave was just a completely arbitrary random example.

The way my non-WP version works is that it DOES in fact have a delimiter of sorts, like this:

www.domain.com/script/template-name/some/list/of/keywords/

so "template-name" becomes the delimiter. This is why I asked about using the post-name itself as the delimiter, as it would accomplish the identical result I believe.

This script is intended to be used for affiliate and other marketing purposes, where there could be an absolutely limitless possible range of keywords, products, virtual page structures, etc. in use so I mustn't place any restrictions on how people structure their URLs, other than minimally so I can identify their "template" or post, as described above.

I can tell these suggestions are on the right track, I just haven't been able to make them actually work in practice. What I'm looking for from this question and for the award, is working code that will accomplish what I need.

Thanks!


Jonathan van Clute comments:

Sorry, it wouldn't be using post-name as the delimiter, it would be using %/permalink%/ since the permalinks could be something other than %postname%


John Cotton comments:

Can you guarantee that category name will always be a single folder (ie not hierarchical)?


Jonathan van Clute comments:

Unfortunately no, I can't ever guarantee what permalink structure might be in use, or how the categories of a specific site might be set up.


John Cotton comments:

In which case, it'll cost more than $50 for me to write the code.

What you want needs some careful thought as, in essence, a whole series of scenarios have to be catered for in the code and that isn't a 30 minute job. There's potential for a performance nightmare as the url needs stepping along, directory by directory, finding the category.


Jonathan van Clute comments:

Hmmm... if I'm understanding you, you're suggesting that stepping through the URL to find the active category is a problem?

Unless I'm missing something, that's probably one of the easier parts and I've already done it in my previous approach. I did a simple backwards for loop, examining each part of the URL as it went... something like this:


$uriparts = array_filter(explode("/",URI_PATH));
for($i=count(array_filter($uriparts)+1;$i>0;$i--){
foreach($uriparts as $part){
$permalink .= '/' . $part;
}
echo( "\n<br>this postid = ".url_to_postid($permalink));
}

Haven't seen any performance problems at all. But maybe I'm not interpreting your concern correctly? Would be slightly different of course as this is only checking url_to_postid and not looking for a category, but the same principal holds I would think...?


John Cotton comments:

You can't use url_to_postid...it checks the value against the rewrite rules!

That's what I was trying to tell you - by using a rewrite rule that matches most URLs, you have to stop using wordpress to do the work and do it yourself. Basically, it's a customised version of url_to_postid that needs writing.


Jonathan van Clute comments:

I wasn't suggesting using url_to_posid, was merely illustrating a simple way to loop through the URL elements. What to compare them against is another issue obviously.

A "custom version of url_to_postid" would do what I need. url_to_archive() would have solved my whole problem... if it could be done.


John Cotton comments:

<blockquote>url_to_archive() would have solved my whole problem... if it could be done</blockquote>

Anything can be done, it's just a question of cost - both of time to do it and resources used to run it on a request.

As I've said, I'm happy to code something up but it's more than $50. However, I've given you all the code structure, you just need to expand on what you've got from your last version - as you say, there are a series of tests that you can apply to each url and you will be able to determine what page is being requests and what the keywords are with a set of conditional statements.








Jonathan van Clute comments:

OK then, what am I looking at then in terms of cost for you to code this? That's what I'm looking for. Your commentary has been interesting and appreciated, but that's not why I'm here... I can get that at Stackexchange or plenty of other places. I'm here because I hoped to be provided with some working code for a reasonable fee. At this point I'm tired of messing with it, and am willing to pay for the code to be written if it's within my budget.

2011-10-10

Abdessamad Idrissi answers:

Did u have a look at [[LINK href="http://wordpress.stackexchange.com/questions/5413/need-help-with-add-rewrite-rule/5478#5478"]]Some background on rewrite rules[[/LINK]]?

Use this regex to rewrite the URL
.*?/(\w+)/.*?/(\w+)/$

Input :

www.example.com/first_folder/some/additional/bunch/of/folders/last_folder/

Output :

$1 prints first_folder
$2 prints last_folder


Jonathan van Clute comments:

Thanks for the link, I read that article but was not able to follow it. Rewriting and regex makes my head spin. I copy/pasted the sample code (as well as other examples I've found in my researching) and tried working with it, but couldn't even replicate their results before altering it to suit my needs.

2011-10-10

Luis Cordova answers:

I honestly think you are trying to solve the problem the hardest possible way. If your routing problem gets overly complex and you really need flexibility to debug, troubleshoot, and modify it over time, I fully recommend integrating it with a routing component like symfony2's routing component which works standalone as well. This is a complex task at first but it will pay off. It will definitely solve many of your problems and let you hook a great framework into your app. I could be wrong though but better than all if this mess keeps going on and on.