Cleaning up Friendly urls with one simple rule

Discuss practical ways rearrange URLs using mod_rewrite.

Cleaning up Friendly urls with one simple rule

Postby TeckniX » Wed Dec 17, 2008 3:32 pm

I'm sure richardk will jump on this, since he's been helping pretty much everyone here, unless someone else feels like they have the answer.

Here is my current mod_rewrite code:
Code: Select all
RewriteEngine on

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^level2=([^&]+)(?:&level3=([^&]+)(?:&level4=([^&]+)(?:&level5=([^&]+))?)?)?(&.*)?$ [NC]
RewriteRule ^(index\.php)?$ /%1/%2/%3/%4/? [R=301,L]



right now if i go to:
/index.php?level2=mypage
i get redirected to:
/mypage////

I'm just trying to clean up the 'extra' //// at the end of a url when there is no level3, level4, level5 type of deal.

Not sure how this can be handled without repeating rules. Any help would be appreciated!

Thanks.
TeckniX
 
Posts: 38
Joined: Tue Mar 27, 2007 12:18 pm

Postby richardk » Wed Dec 17, 2008 3:43 pm

Code: Select all
^level2=([^&]+)(?:&level3=([^&]+)(?:&level4=([^&]+)(?:&level5=([^&]+))?)?)?(&.*)?$

You can't really do it in one line because query string variables can be in any order.

Will there always be the smaller numbered variables (eg. if there is a level4 will there always be level2 and level3)?
richardk
 
Posts: 8800
Joined: Wed Dec 21, 2005 7:50 am

Postby TeckniX » Thu Dec 18, 2008 7:10 am

Hmm that's what I was afraid of. But yes, if there is a level4 there will always be a level3 and a level2.

Do you have something in mind?
TeckniX
 
Posts: 38
Joined: Tue Mar 27, 2007 12:18 pm

Postby richardk » Thu Dec 18, 2008 8:21 am

This should work, but it is a bit long
Code: Select all
Options +FollowSymLinks

RewriteEngine On

RewriteCond %{QUERY_STRING} ^(.*&)?l1=([^&]+)(&.*)?$ [NC]
RewriteCond %2/&%{QUERY_STRING} ^([^&]+)&(.*&)?l2=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %2/ ^(.+)$
RewriteCond %1%3/&%{QUERY_STRING} ^([^&]+)&(.*&)?l3=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %1%3/ ^(.+)$
RewriteCond %1%3/&%{QUERY_STRING} ^([^&]+)&(.*&)?l4=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %1%3/ ^(.+)$
RewriteCond %1%3/ !//
RewriteRule ^(index\.php)$ /%1%3/? [R=301,L]

You might want to redirect in your index.php file instead.
richardk
 
Posts: 8800
Joined: Wed Dec 21, 2005 7:50 am

Postby TeckniX » Thu Dec 18, 2008 10:20 am

ok I would've never though of doing it like that - I'll have to dissect it to understand what's going on. haha


Thanks richardk.
TeckniX
 
Posts: 38
Joined: Tue Mar 27, 2007 12:18 pm

Postby richardk » Thu Dec 18, 2008 1:03 pm

I think should be
Code: Select all
Options +FollowSymLinks

RewriteEngine On

RewriteCond %{QUERY_STRING} ^(.*&)?l1=([^&]+)(&.*)?$ [NC]
RewriteCond %2/&%{QUERY_STRING} ^([^&]+)&(.*&)?l2=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %2/ ^(.+)$
RewriteCond %1%3/&%{QUERY_STRING} ^([^&]+)&(.*&)?l3=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %1%3/ ^(.+)$
RewriteCond %1%3/&%{QUERY_STRING} ^([^&]+)&(.*&)?l4=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %1%3/ ^(.+)$
RewriteCond %1%3 ^(.*[^/])/*$
RewriteCond %1/ !//
RewriteRule ^(index\.php)?$ /%1/? [R=301,L]


I'll have to dissect it to understand what's going on.

Code: Select all
RewriteCond %{QUERY_STRING} ^(.*&)?l1=([^&]+)(&.*)?$ [NC]

Makes sure that l1 exists and has a value. It is passed on to the next line.

Code: Select all
RewriteCond %2/&%{QUERY_STRING} ^([^&]+)&(.*&)?l2=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %2/ ^(.+)$

If l2 exists and has a value it is matched and passed on (along with l1). If it doesn't only l1 is passed on.

Code: Select all
RewriteCond %1%3/&%{QUERY_STRING} ^([^&]+)&(.*&)?l3=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %1%3/ ^(.+)$
RewriteCond %1%3/&%{QUERY_STRING} ^([^&]+)&(.*&)?l4=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %1%3/ ^(.+)$

Builds a string (in %1%3) of "level1/level2/level3/level4/" (even if level2, 3 or 4 are empty).

Code: Select all
RewriteCond %1%3 ^(.*[^/])/*$

Strips off trailing slashes. (%1)

Code: Select all
RewriteCond %1/ !//

Makes sure there aren't any double slashes (for example if level2 was empty or missing: "level1//level3/level4/").
richardk
 
Posts: 8800
Joined: Wed Dec 21, 2005 7:50 am

Postby TeckniX » Thu Dec 18, 2008 3:44 pm

Just wanted to update this one last time, as I finally got to understand most of the logic behind it.

I added an extra level which didn't keep things too clean but works well.

Here's what I have:

Code: Select all
RewriteEngine on

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{QUERY_STRING} ^(.*&)?level2=([^&]+)(&.*)?$ [NC]
RewriteCond %2/&%{QUERY_STRING} ^([^&]+)&(.*&)?level3=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %2%3 ^([^&]+)()()(&.*)?$
RewriteCond %1%3/&%{QUERY_STRING} ^([^&]+)&(.*&)?level4=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %1%3%4 ^([^&]+)()()(&.*)?$
RewriteCond %1%3/&%{QUERY_STRING} ^([^&]+)&(.*&)?level5=([^&]+)(&.*)?$ [NC,OR]
RewriteCond %1%3%4 ^([^&]+)()()(&.*)?$
RewriteCond %1%3 !//
RewriteRule ^(index\.php)$/%1/?%4 [R=301,L]



The extra %4 was needed in order to capture the remaining querystring and not end up with the entire original querysting.

It's not pretty but I think it does the job quite well.


Thanks again for all of your help! It's so nice to be able to come here for help and get well what I need :)
TeckniX
 
Posts: 38
Joined: Tue Mar 27, 2007 12:18 pm

Postby richardk » Fri Dec 19, 2008 6:00 am

That won't work, /index.php?level3=b&level4=c&level2=a redirects to /a/b/c/?&level2=a. Doing this with PHP would be a lot easier to follow.

This is a bit closer but is not thoroughly tested.
Code: Select all
Options +FollowSymLinks

RewriteEngine On

RewriteCond %{QUERY_STRING} ^(.*&)?level2=([^&]+)(?:&(.*))?$ [NC]
RewriteCond %2/&%1%3 ^([^&]+)&(.*&)?level3=([^&]+)(?:&(.*))?$ [NC,OR]
RewriteCond %2/&%1%3 ^([^&]+)&(.+)$
RewriteCond %1%3/&%2%4 ^([^&]+)&(.*&)?level4=([^&]+)(?:&(.*))?$ [NC,OR]
RewriteCond %1%3/&%2%4 ^([^&]+)&(.+)$
RewriteCond %1%3/&%2%4 ^([^&]+)&(.*&)?level5=([^&]+)(?:&(.*))?$ [NC,OR]
RewriteCond %1%3/&%2%4 ^([^&]+)&(.+)$
RewriteCond %1%3/&%2%4 ^([^&]*[^/&])/*&(.+)$
RewriteCond %1/ !//
RewriteRule ^(index\.php)?$ /%1/?%2 [L]


Untested with PHP
Code: Select all
<?php

// It must not be part of a mod_rewrite request (to stop loops)
// and level2= must exist and have a value.
if(getenv('REDIRECT_STATUS') === false && isset($_GET['level2']) && !empty($_GET['level2))
{
   // Add it to the new URL.
   $mr_new_url = '/' . $_GET['level2'];
   // Remove it from the $_GET array.
   unset($_GET['level2']);

   // Continue until a leveln is not found.
   // (So there will be no double slashes.)
   if(isset($_GET['level3']) && !empty($_GET['level3))
   {
      $mr_new_url .= '/' . $_GET['level3'];
      unset($_GET['level3']);

      if(isset($_GET['level4']) && !empty($_GET['level4))
      {
         $mr_new_url .= '/' . $_GET['level4'];
         unset($_GET['level4']);

         if(isset($_GET['level5']) && !empty($_GET['level5))
         {
            $mr_new_url .= '/' . $_GET['level5'];
            unset($_GET['level5']);
         }
      }
   }
   // All the levels have been removed so $_GET can be impoded.
   $mr_new_querystring = implode('&', $_GET);

   // Only add a ? if there is a query string to append.
   if(!empty($mr_new_querystring))
   {
      $mr_new_querystring = '?' . $mr_new_querystring;
   }

   // Redirect
   header('Location: http://' . getenv('HTTP_HOST') . $mr_new_url . '/' . $mr_new_querystring, true, 301);
   exit();
}

It could probably be simplified further.
richardk
 
Posts: 8800
Joined: Wed Dec 21, 2005 7:50 am


Return to Friendly URLs with Mod_Rewrite

Who is online

Users browsing this forum: No registered users and 27 guests

cron