php tutorial - separate your application logic from your presentation logic

Friday, 9 January 2009


There are many good reasons to separate your programs logic from the presentation code; readability, reusability and the ability to make changes in one part without having to make changes to the other half.

Recently there has been a lot of talk (and adoption) of MVC frameworks (heck even MS has a framework for .net now). With some of the top php programmers helping to build these frameworks there are a lot of good lessons we can learn, especially when it comes to separating our logic from the presentation code, one of the main aims of MVC. Of course you can just grap one of these and not have to worry about reinventing the wheel, but there are a few good reasons that you would want to know how to do this:

1. The size of your project doesn't warrant having an entire framework built in to your site / app
2. You are just starting to learn about MVC concepts and you want to learn more
3. You want to be able to build it in a way that is useful and familiar for reuse in your own apps

Note: That in order for this to work you will need to have a web server with some form of mod-rewrite with htaccess support available.

This is the basic outline of what happens:

Our URL: mysite.com/fishing/home

request is made -> htaccess redirects to a base file (e.g. index.php) -> index.php interperates request variables and redirects to appropriate file for processing (e.g. -> fishing.php) -> our processing script reads in the appropriate template (html file) and replaces 'place holders' in the template with our processed content -> script echos out the file with all content

This might sound a little confusing at first but we can break it down with an example.

We start with our web server with htaccess available.

The files we need are as follows:

/.htaccess
/index.php
/controllers/fishing.php
/views/fishing/home.html

Please note the structure, it is important that we have a good structure of where to place our files. A good structure helps us to locate files, rather than having everything in the same folder. Because we may have two similar requests e.g. mysite.com/fishing/home and mysite.com/diving/home we need to have another tier in our views folder i.e.

/views/fishing/home.html
/views/diving/home.html

So from another perspective the flow works as thus: .htaccess -> index.php -> /controllers/fishing.php (reads in ->) /views/fishing/home.html

Looking at our URL we can see this is directly related

mysite.com/fishing/home ===> (site)/(controller)/(view)

So we now have our application logic in the controller and the presentation in the view and this is directly related to the request made by the user... neat!

So lets have a closer look at each of the files and see how this all comes together.

Firstly our .htaccess. We want to forward the request on to index.php and grab the extra parts of the request, the /fishing/home part of the URL.

Here is what the .htaccess file looks like:

RewriteEngine On // Turn rewrite engine on
RewriteCond %{REQUEST_FILENAME} !-d // If the request is for a real directory go to that directory
RewriteCond %{REQUEST_FILENAME} !-f // If the request is for a real file go to that file
RewriteRule ^(.*)$ index.php?url=$1 [QSA,L] // This is where the magic happens, it grabs our /fishing/home, redirects us to index.php and then appends the /fishing/home as follows: index.php?url=/fishing/home
// In regards to the QSA I think this forum entry explains it best.
// L tells the rewrite engine not to apply any more rules

So we now have the first step in place, we get our url stored in the $_GET['url'] variable and we have been redirected to index.php by our htaccess file.

The next step is our index.php. We want to inerpret what is stored in $_GET['url'] and redirect to the correct controller. This is only a simple example but you should make sure when you implement that you check that what comes in through $_GET is clean and you should always make sure someone can't compromise your system just by typing in a url i.e. mysite.com/fishing/delete_everything

You should also make sure you have error handling in your application for when someone specifies an incorrect controller/view or doesn't specify one or the other.

Let's have a look at our index.php:


<?php

$url = $_GET['url']; // Remember to clean this is a real-world situation
$parts = split('/', $url); // Going to store parts of our request url in $parts

$controller = $parts[0]; // In our case fishing.php

// Insert URL checking here to see if view / controller exists

$controllerloc = DOCROOT . 'controllers\' . $controller . '.php'; // Set up a link to the controller location

include($controllerloc); // Include our controller

?>


There are much better ways to implement this, but this should give you a good overview of what should happen. In most good apps the controller will be an object, allowing for better handling etc.

Now lets take a look at fishing.php in the controllers folder. We want this folder to load our template, do some manipulation and then display our page.

// Firstly we want to get our view from the url
$url = $_GET['url']; // Remember to clean this is a real-world situation
$parts = split('/', $url); // Going to store parts of our request url in $parts

$view = $parts[1];
$vars = $parts[2]; // In case any extra variables are passed through e.g. mysite.com/fishing/edit/54

Now that we have our view we can call that method from within the controller, as previously mentioned the controller should be an object with all of the 'views' as methods. I am doing it as functions for simplicity sake.



<?php

// Firstly we want to get our view from the url
$url = $_GET['url']; // Remember to clean this is a real-world situation
$parts = split('/', $url); // Going to store parts of our request url in $parts

$view = $parts[1];
$vars = $parts[2]; // In case any extra variables are passed through e.g. mysite.com/fishing/edit/54

// Check function exists
if (function_exists($view)) {
call_user_func($view);
}


function home() {
$viewloc = DOCROOT . 'views\fishing\home.html';

// Check view exists

$viewHTML = file_get_contents($viewloc);

// *** do some processing ***

echo $viewHTML;
}

function add() {

}

function check() {

}

// etc. etc. ... other functions


?>



So you can see that we grab the view, do some procesing and then echo our our HTML. So what is going on inside the 'do some processing part'?

This is where the application logic goes in, you may read some information from the database, trigger another process and then prepare other information ready for display. So how do we get this information in to our HTML in the correct place without have the two mashed together? There are a few points of information:

1. You will often need your controller to generate some HTML code, this is fine just as long as it's not generating the whole header or whole sections of HTML code.
2. You will often need to perform some manipulation of the information that comes from the controller inside the view. That is also fine. The way this is normally done is rather than getting the view using file_get_contents, we simply include the view file and have some small php calls e.g. looping over data results inside there.
3. To get your code from the controller into the view, the nicest way to do this is by using placeholders inside your view file and using str_replace to replace your place holders with the code.

How to use place holders and str_replace:

You can decide how your placeholders will look like i.e [-how_happy_am_i-] Once you have read your file in using file_get_contents you have your information stored in a variable as follows:


$viewHTML = file_get_contents($viewloc);
$mymood = 'damn happy';


Then to replace the place holder with your mood you simply call str_replace as such:


str_replace('[-how_happy_am_i-]', $mymood, $viewHTML);


There we go, easy as pie. Then we can have our calculate variables injected into our HTML code without having to have all that logic stuck inside... sweet!

Of course if you are going to use place holders, it's always good to have a function to clear out any remaining placeholders in the code before echoing to the screen in case you leave them behind.


Many places are now adopting frameworks and code separation as it has many benefits and it is a huge plus for readability and later updates. It's a good tool to have in your belt and many job advertisiments are asking for people with understanding of MVC.
blog comments powered by Disqus