Unobtrusive Ajax Navigation – Version 3

June 15, 2009 13:24 by scibuff

Last week we continued the tutorial on Unobtrusive AJAX Navigation by cleaning up our Javascript code as well as adding functionality which enabled users to bookmark individual pages and use the “Back” and “Forward” browser buttons for navigation. This time, we’ll focus on cleaning up the back-end scripts to make the solution easily maintainable and expandable. Check out the fully functional demo and/or download the files for this tutorial.

There are two equally good approaches to achieve our goal. One is using simple server-side include (SSI) instructions to keep common content in separate files and include them into our pages at the time when it is needed. The second approach is via Object Oriented (OO) PHP. I am a big fan of OO but too often I see it misused by self-pronounced programmers who naively think that throwing OO concepts on everything makes them better coders (nothing could be further from the truth). There is place and time for deciding to “complicate” your file structure but unless you’re about to create a solution of the size of WordPress (for example) you should think really hard about the pro’s and con’s. Unless your intention is to build a robust, easily extendable and maintainable product with support for hundreds of thousands of users, good old procedural scripting will do the job just as well (if not better).

So without much further ado, let us look at the back-end code from the previous projects:

The first thing that is immediately obvious is that the majority of code in “index.php” and “about.php” is identical. Let’s split this code into appropriate files. Create a directory called “inc” and place the following “.htaccess” file in it:

Order Allow,Deny
Deny From All

All this file does is it prevents any sort of (GET, POST, HEAD etc) requests made directly to the /inc directory. Next, we create a file called “header.php” and place the “print_header” function in it:

<?php
 
function print_header(){
?>
 
	...
	<body id="version-1-0">
 
		<!-- WRAPPER - START -->
		<div id="wrapper">
 
			<!-- HEADER - START -->
			<div id="header">
 
			<h2>Unobtrusive Ajax Navigation - Version 3.0</h2>
 
			<ul >
				<li><a href="index.php">Home</a></li>
				<li><a href="about.php">About</a></li>
			</ul>
 
 			<div></div>
 		</div>
 		<!-- HEADER - END -->
 
 		<!-- CONTENT - START -->
 		<div id="content">
 
<?php
}
?>

Similarly, we create “footer.php” and place the “print_footer” function code in it.

 <?php
 
function print_footer(){
?>
 	 	</div>            
 		 <!-- CONTENT - END -->
 
 		 <!-- FOOTER - START -->
 		 <div id="footer">
 	 		 <p>Footer</p>
 	 	</div>
 	 	<!-- FOOTER - END -->
 	 	</div>
	</body>
</html>
<?php
}
?>

Then create another two include files, one for the content of “index.php” and one for the content of “about.php”. Let’s name them “index.inc.php” and “about.inc.php” respectively, and place the “print_content” functions in them, like so

<?php
 
function print_content(){
?>
	<h2>Homepage</h2>
	<p>Lorem ipsum ...</p>
<?php
}
?>
<?php
 
function print_content(){
?>
	<h2>About</h2>
	<p>Lorem ipsum ...</p>
<?php
}
 
?>

Next, create “content.php” and place the code from above the “print_header” function in “index.php” (or about.php) into a newly created “print_page” function, like so

function print_page( $page ){
 
	require_once("inc/header.php");
	require_once("inc/footer.php");
 
	require_once( $page );
 
	// if the page is requested by AJAX
	if ( isset($_REQUEST['ajax']) && $_REQUEST['ajax'] == "true" ){
		print_content();
	}
	else {
		print_header();
		print_content();
		print_footer();
	}
}

Perhaps, this new function requires a bit of explaining: We want to remove all (as much as possible) code duplicity, so create a function which will print a page content, just as we did before (depending on whether the request came directly from a browser or was requesting by our AJAX Javascript code). The only difference is that we use the “$page” argument to tell the function which page needs to be printed. The script will use the “print_content” function from the appropriate file.

Now, whenever you see something resembling

require_once( $content_inc );

your red alert flashing light should immediately go off. This is potentially a huge security risk: if someone could make the value of “$page” to be “.htaccess” or “.htpasswd” the user could read (encrypted) passwords and see other site settings. In the particular use of the construct, it is unlikely (nothing is impossible) that anyone would be able to exploit it. Nevertheless, it is possible that sometime in the future, to extend functionality or for some other purpose, the smallest modification to the code could leave us vulnerable against an attack. In short, the best practice is NEVER to trust the user, especially when a potential security flaw can easily be fixed by restricting the possible value set. Add the following lines before the function declaration:

define("PAGE_HOME", 0);
define("PAGE_ABOUT", 1);
 
define("PAGE_HOME_SSI", "home.inc.php");
define("PAGE_ABOUT_SSI", "about.inc.php");

change the function declaration to

function print_page( $page = PAGE_HOME )

and add the following switch statement:

	switch( $page ){
		case PAGE_ABOUT : {
			require_once( PAGE_ABOUT_SSI );
			break;
		}
		case PAGE_HOME : {
			// fall through to default
		}
		default : {
			require_once( PAGE_HOME_SSI );
			break;
		}
	}

The few extra lines of code is all that is necessary to prevent the possibility of a large headache (at best) in the future.

Now, all that’s left is to use the newly created files and functions in our original scripts. Thus, we go to “index.php” and place the following lines of code in it:

<?php
require_once("inc/content.php");
print_page( PAGE_HOME );
?>

Similarly, in “about.php”

<?php
require_once("inc/content.php");
print_page( PAGE_ABOUT );
?>

We will conclude this tutorial by adding a page called “Contact”. First of all, create a “contact.php” in the main folder and place the following code inside it:

<?php
require_once("inc/content.php");
print_page( PAGE_CONTACT );
?>

Then open the “content.php” inside the “inc” folder and add

define("PAGE_CONTACT", 2);
define("PAGE_CONTACT_SSI", "contact.inc.php");

as well as

		case PAGE_CONTACT: {
			require_once( PAGE_CONTACT_SSI );
			break;
		}

into the switch statement. Then, let’s add the Contact Page to our navigation by placing the following into the “header.php”

<li><a class="ajax-link" href="contact.php">Contact</a></li>

Finally, we need to create a “contact.inc.php” inside the “inc” directory with a “print_content” function:

<?php
 
function print_content(){
?>
	<h2>Contact</h2>
 
	<p>Address Line 1</p>
	<p>Address Line 2</p>
	<p>City, Zip Code</p>
	<p><strong>Country</strong></p>
 
<?php
}
?>

Next time, we will look at creating Unobtrusive AJAX Navigation as a part of a complex product (using OO) as well as making URL more user friendly (although search engine are perfectly capable of indexing links with query parameters there is an elegant way to make links more user friendly).

Meanwhile, check out a fully functional demo of version 3 and/or download the files for this tutorial.

Be Sociable, Share!

Comments

10

Elliott

Just curious when you are going to post version 4. I can’t wait any longer!

scibuff

yeah, I’ve been working on that for some time … but not exactly 100% sure what I want to focus on. Do you have any specific question you’d like to have answered?

p.s. if you’d like to see version 3 in live action, check out these sites of mine I just made yesterday for a few astro twitter events:

http://www.scibuff.com/thegreatlookup/
http://www.scibuff.com/space-shuttle/

Elliott

Well, I was playing with your setup on my web site, and the smooth degradation and clean code is something I’ve been after for a long time. I just don’t have a lot of the background to figure this stuff out myself. I think version 3 is in a good place, but making the URLs more friendly is something that would make it almost ideal. I’m always concerned about site maintenance, even though I just play around with web design without any real content (just in case I decide to start putting stuff up), and that fact has been hindering me from moving in the direction your tutorials seem to be heading. Your setup is one of the cleanest I’ve seen, but I’m still concerned about updating code in three different files to add a single page. What are your thoughts on improving the maintainability in that respect?

Dev

Hi,
Great examples and very well coded. Was wondering if you could code it using jquery’s history plugin.

Chris

How about adding some nice fade in/fade out effects and a loading animated gif?

scibuff

Chris, although I don’t have it in the tutorial, I did indeed do exactly that. The following sites all use this ajax 3.0 version with animation and loading gif:
Space Shuttle Gallery, Space Station Gallery, Moonwatch Gallery, etc.

scibuff

Chris, here’s a version 3.5 demo with a simple loading animated gif: http://www.scibuff.com/dev/ajax/ajax-version-3.5/

Jason

I decided to apply this to my portfolio site – I’m in the process of developing it and wanted to apply lightbox to the site to display my work. I found out that lightbox and the jquery used in the ajax file are conflicting. I somewhat found out how to fix it, but I’m kind of new with ajax, so I don’t know how to apply it.

I was hoping someone would be able to help. This article here, http://forums.obdev.at/viewtopic.php?f=6&t=2951, explained to me what was wrong, but I was wondering if anyone could give me any further explanation.

PS. Great tutorial.

Marcello Barnaba

Hi Tomas,

what you implemented here with raw PHP comes for free if you’d used Rails, Django or Symfony, via layouts and partials :-).

Anyway, I like your approach of unobtrusively “upgrading” to ajax navigation if the browser supprts it, and I’m glad that you liked on FriendFeed the jquery ajax-nav framework that implements the same approach. You know, I don’t like much the way Google is pursuing with their AJAX crawler, as it isn’t compatible with older browsers and crawlers. :-)

BTW, currently the ajax-nav code has been tested only with a Rails REST backend: would you mind trying it with your PHP code? It would be nice to have multiple demos for it.

Again, thanks for your kind words; looking forward to our future interaction.

PS. long live the flying spaghetti monster! :-)

dtouch

I used this in site. it works pretty well in FF, Chrome and Opera
But Just the home page loads in 1e9..
On clicking ajax links.. I get a blank page!!

Leave your comment:
XHTML:You can use these tags: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> . * required