Project “Ragtime”: dynamic URLs
Now that you know about arrays and GET variables, let's review our little project site and see what can be improved.
What we have so far
In case you skipped the previous two stages of the Ragtime web-site development, check out here and here and the last demo here.
In the previous implementations we had some PHP scripts (like aboutus.php) that also include common parts of the pages - header, footer, menu. One kind of annoying this is that for every new page, you have to copy/paste some PHP code and repeat it in every page. This is the code that includes header.inc.php and footer.inc.php. Let's improve this.
The new file layout
In the new implementation, we remove any PHP code from all content pages (contact.php, aboutus.php, etc). Since these files now will contain only HTML, it's no longer necessary to have them as PHP scripts, so we'll rename them to *.html. So we'll have these static content pages:
- products.html (was products.php)
- aboutus.html (was aboutus.php)
- contact.html (was contact.php)
- home.html - This is the home page, was index.php
We'll also have the same include files, although they'll be slightly changed:
- header.inc.php
- footer.inc.php
- menu.inc.php
The big changes are in index.php, it will now serve as sort of a controller script that takes user input and decides which files to include. The user input will be received through a GET parameter named "page". So we'll have the following pages with the corresponding URLs:
| Page | URL |
|---|---|
| Home (index) page | index.php?page=home |
| Products | index.php?page=products |
| Contact Us | index.php?page=contact |
| About Us | index.php?page=aboutus |
The content pages
These are now really simple static HTML files.
home.html
<h1>Welcome to Ragtime!</h1> <p>Select a link from our nice menu</p>
products.html
<h1>Products</h1> <p>Check out our wide selection of tires. We have:</p> <ul> <li>Winter tires</li> <li>Summer tires</li> <li>All-season tires</li> </ul>
contact.html
<h1>Contact Us</h1> <div id="hcard-Ragtime" class="vcard"> <a class="url fn" href="http://www.ragtimebg.com">Ragtime</a> <div class="adr"> <span class="locality">Sofia</span>, <span class="country-name">Bulgaria</span> </div> <div class="tel">1-888-123-4567</div> </div>
aboutus.html
<h1>About Us</h1> <p>This is the "about us" page, always very popular place.</p> <p>Our company was found in year 1467 by ...</p>
The index script
This script index.php will do most of the work. The script starts by defining an array calles $pages. This array will hold information about all the pages in the site. It's an associative array and its keys are the page identifiers. Each page identifier has a corresponding array that has the keys title and menu. The title value will be used for the <title> tag and the menu value will be used for the navigation menu. If menu is missing, the value in title will be reused.
<?php
$pages = array(
'home' => array(
'title' => 'Welcome to Ragtime!',
'menu' => 'Home'
),
'products' => array(
'title' => 'Products',
),
'contact' => array(
'title' => 'Contact us',
),
'aboutus' => array(
'title' => 'About us',
),
);
Next, we want to populate a value in a $page variable. This variable will hold the identifier of the page the user has requested. We check if the $_GET['page'] input parameter has a value using the function empty(). If not, we default to the homepage.
// is there a page ID passed in GET
if (empty($_GET['page'])) {
$page = 'home';
} else {
$page = $_GET['page'];
}
Now that we have $page defined, let's validate its value. We expect that the regular user will just follow our links and will pass values for the $_GET['page'] parameter such as "home", "products", etc. But there will be users that will try to play with this parameter, passing strange values. "Never trust user input" is the mantra, so we must validate. We do this by simply checking if the page ID passed by the user exists as a key in the pre-defined $pages array. If it does, we're fine, otherwise we reset $page to "home".
// is this a valid page ID
if (empty($pages[$page])) {
$page = 'home';
}
Finally, we define a $title variable (to be reused in the header.inc.php for the <title> tag) and we include header, footer and the static content page that was requested. If for example the request was index.php?page=aboutus, we end up including aboutus.html
// title of the page $title = $pages[$page]['title']; include 'header.inc.php'; include $page . '.html'; include 'footer.inc.php'; ?>
Header and footer
The header and the footer contain only repeating parts, common to all pages.
header.inc.php uses the $title variable defined in index.php for the HTML title tag, it also uses the $page variable to set an ID attribute in the HTML body tag (we'll see why in a second). The header also includes menu.inc.php.
Here's the content of the header.inc.php include script
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>Ragtime > <?php echo $title; ?></title> <link rel="stylesheet" href="ragtime.css" type="text/css" /> </head> <body id="page-<?php echo $page; ?>"> <?php include 'menu.inc.php'; ?> <div id="content">
footer.inc.php only closes tags.
</div> </body> </html>
menu.inc.php
In the menu.inc.php we simply loop through the $pages array (defined in index.php) in order to produce a list of menu items. To make the code a little bit more readable, we have a little template that is used for the LI tags. Then in every iteration of the loop we call the function str_replace() to replace the placeholders {PAGE} and {TITLE} with real values.
<ul id="menu">
<?php
$item_tpl = '<li><a href="index.php?page={PAGE}" id="menu-{PAGE}">{TITLE}</a></li>';
foreach ($pages AS $key => $data) {
$menu_item = str_replace('{PAGE}', $key, $item_tpl);
if (!empty($data['menu'])) {
$menu_item = str_replace('{TITLE}', $data['menu'], $menu_item);
} else {
$menu_item = str_replace('{TITLE}', $data['title'], $menu_item);
}
echo $menu_item;
} // end foreach
?>
</ul>
After this code is executed, the HTML code for the navigation menu will look like:
<ul id="menu"> <li><a href="index.php?page=home" id="menu-home">Home</a></li> <li><a href="index.php?page=products" id="menu-products">Products</a></li> <li><a href="index.php?page=contact" id="menu-contact">Contact us</a></li> <li><a href="index.php?page=aboutus" id="menu-aboutus">About us</a></li> </ul>
Highlighting the current menu item
As you see we defined an id attribute in the body tag (like "page-products") and also an id in the navigation menu. This allows the use of CSS in order to highlight the current menu item by using:
#page-aboutus #menu-aboutus,
#page-home #menu-home,
#page-products #menu-products,
#page-contact #menu-contact {
background: CornflowerBlue;
}
So the navigation menu item will have a different color but only on the page that has the same id. It's a small trick that can help a lot to have CSS do some work instead of using an if in the PHP code.
Demo and code
An SEO improvement with .htaccess
The site is very nice now and have a bunch of static files (which can be edited by someone with HTML experience only, no PHP knowledge required). We use dynamic URLs and have a little framework to load the static files per user requested.
A potential issue is that it's believed that some search engines don't like dynamic URLs very much and sometimes ignore everything after the ? symbol. It's not quite clear how much of an issue that is, but why risk the search engine popularity of the site when we can quickly correct this, using URL rewrites.
I'm not going to describe the URL rewrites in detail here, but the idea is that Apache has this ability to turn some publicly visible URLs into others that can be used internally. In our case we want that a request to home.html to be treated the same as a request to index.php?page=home. To achieve this you create an .htacess file with the following contents:
Options +FollowSymLinks RewriteEngine on RewriteRule ^(.*).html$ index.php?page=$1
We also need to change the "template" we used in the menu.inc.php in order to produce the new type of URLs:
$item_tpl = '<li><a href="index.php?page={PAGE}" id="menu-{PAGE}">{TITLE}</a></li>';
becomes
$item_tpl = '<li><a href="{PAGE}.html" id="menu-{PAGE}">{TITLE}</a></li>';
The new code and demo:
Buy:Prevacid.Human Growth Hormone.Synthroid.Nexium.Zyban.Petcam (Metacam) Oral Suspension.Prednisolone.Accutane.Lumigan.Actos.Arimidex.100% Pure Okinawan Coral Calcium.Mega Hoodia.Retin-A.Valtrex.Zovirax….