views:

320

answers:

3

Hi,

I am not a PHP developer at heart and I have been asked to perform some SEO on an existing PHP website.

The first thing I noticed was the ugly URLs so I want to get these to rewrite to something more informative. Here are all the possible patterns:

/index.php?m=ModuleType&categoryID=id
/index.php?m=ModuleType&categoryID=id&productID=id
/index.php?page=PageType
/index.php?page=PageType&detail=yes

So basically what I want to do is convert these into something like:

/ModuleType/Category
/ModuleType/Category/ProductName
/Page
/Page

I haven't used mod_rewrite before any advice or examples would be great!

Thanks.

+1  A: 

mod_rewrite would rather be used to do the opposite: rewrite requests of /ModuleType/Category/ProductName internally to /index.php?m=ModuleType&categoryID=id&productID=id. Using the new URLs in the documents is the job of your application.


Edit    Here’s an example of how a function might look like that turns your parameterized URLs into the new ones:

function url($url, $rules) {
    $url = parse_url($url);
    parse_str($url['query'], $url['query']);
    $argNames = array_keys($url['query']);
    foreach ($rules as $rule) {
        if ($rule[0] == $url['path'] && array_keys($rule[1]) == $argNames) {
            $newUrl = $rule[2];
            foreach ($rule[1] as $name => $pattern) {
                if (!preg_match('/'.addcslashes($pattern, '/').'/', $url['query'][$name], $match)) {
                    continue 2;
                }
                $newUrl = str_replace('<'.$name.'>', $match[0], $newUrl);
            }
            return $newUrl;
        }
    }
    return $url;
}

$rules = array(
    array(
        '/index.php',
        array('m'=>'.*', 'categoryID'=>'.*', 'productID'=>'.*'),
        '/<m>/<categoryID>/<productID>'
    )
);
echo '<a href="' . url('/index.php?m=ModuleType&categoryID=categoryID&productID=productID', $rules) . '">/ModuleType/Category/ProductName</a>';
Gumbo
Ok how would I do this then? How would I convert the parameterized queries into nicer URL's?
James
@James: Your web application should do that. Typically you would have a mapping to map from the one form to the other.
Gumbo
Yeah but I am not quite sure how I do this, could you provide an example?
James
@James: The *nice to parameterized* or *parameterized to nice* direction?
Gumbo
@Gumbo, basically the way the website is at the moment is the URLs are displaying as parameterized, so I want them to be displayed nicer.
James
@James: I would write a function that will to the mapping. Describe the map as an array of rules that are array themselves that contain an array of URL parameters and the resulting nice URL. So something like `array( /* first mapping rule: */ array( /* array of parameters: */ array('m', 'categoryID', 'productID'), /* replacement: */ '/<m>/<categoryID>/<productID>'), /* more rules following … */ )`. Then use the `parse_url` to parse the URL and `parse_str` for the URL parameters, look for a corresponding rule in the ruleset and do the replacement.
Gumbo
Could you provide a small example please? i.e. update your answer.
James
Thaks for taking the time to provide that example, appreciate it.
James
Turns out you were correct in terms of achieving this throughout the links on my site, however, the other answers did contribute as I do need the server to translate these new friendly urls.
James
+1  A: 

I'm not a mod_rewrite expert by any means, but this is an example of how I put together the .htaccess for the Image Flair site:

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)/(.*)\.png$ imageFlair.php?mode=$1&userid=$2 [L]
RewriteRule ^(.*)\.png$ imageFlair.php?userid=$1 [L]
</IfModule>

This basically maps:

MODE/USERID.png -> imageFlair.php?mode=MODE&userid=USERID

and

USERID.png -> imageFlair.php?userid=USERID

You should be able to adapt that to your needs, but you may have a couple of issues:

  1. If you want to use "names" rather than IDs on your URL you will need to alter the PHP to accept the names.
  2. You might have an issue with /Page and /ModuleType conflicting, if you wanted to also include more parameters in with Page, unless you can put together a regex that can determine which is which.

Going on the list of URLs you want, this should work, although I won't claim it's the best or only way to do it :-)

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteRule ^(.*)/(.*)/(.*)$ index.php?m=$1&categoryID=$2&productID=$3 [L]
RewriteRule ^(.*)/(.*)$ index.php?m=$1&categoryID=$2 [L]
RewriteRule ^(.*)$ index.php?Page=$1 [L]
</IfModule>

As suggested, you may want to replace the .* with [^/]+ but I had issues with non-matches when I did that, and had no time to troubleshoot, so YMMV :-)

Steven Robbins
Please replace `.*` by `[^/]+` and mark the end of the URL path with `$`.
Gumbo
Hi steve, thanks for the examples! Always find it easier to learn...what is the L parameter at the end of the rewrite rules for?
James
It just tells Apache to stop processing for that request once it's matched the rule.
Steven Robbins
I put that rule in but then all my javascripts where giving errors! Taking it back out they run fine.
James
@Gumbo I had issues with [^/]+ on my box when I was testing, hence the .* - you're probably right on the lack of $ though, my mistake.
Steven Robbins
Your javascript may be relying on the parameters being on the url string then, it's not always a simple thing to retrofit.
Steven Robbins
Ah ok, sorry as I said this isn't my website still trying to figure out how it has been put together!
James
Using `[^/]` instead of `.` is just more explicit and efficient since it avoids backtracking.
Gumbo
Yeah, I know what it does, but it matched nothing when I tried it so I took the quick, dirty and working approach :-)
Steven Robbins
+1  A: 

It's not quite clear from your post what the variables are. But assuming that ModuleType, id (x2) and Page are all variables then the following rules with backreferences should work within a .htaccess file.

RewriteEngine On
RewriteRule ^([^/]+)/([^/]+)$ /index.php?m=$1&categoryID=$2 [L]
RewriteRule ^([^/]+)/([^/]+)/([^/]+)$ /index.php?m=$1&categoryID=$2&productID=$3 [L]
RewriteRule ^([^/]+)$ /index.php?page=PageType [L]
RewriteRule ^([^/]+)/detail$ /index.php?page=PageType&detail=yes [L]

The last one didn't really make sense as you've written it. So instead you can add /detail on the end.

These should slip straight over the top of your existing applications without any modifications to the app. Because they aren't redirecting with [R] it will be transparent to your users.

Dan Carley
Hi Dan, apologies on the last one, I never thought of having to differentiate the 2! That would be an ideal solution for it tho. I will give them a try.
James
Hi I entered your rules into the .htaccess file but they don't seem to be doing anything? Do I need to restart anything? Sorry I am a bit of a novice when it comes to PHP.
James
I've updated it so that it should be suitable to paste straight into an `.htaccess` file.
Dan Carley
I think you where missing a backslash at the start of each rule, I included this in and the error stopped occurring. However, the rules don't seem to be taking effect.
James