Adapting the Model-View-Controller pattern for PHP

Much discussion has gone into the relevance of the model-view-controller (MVC) pattern in web development. While it may have its uses, when implemented in its full level of abstraction, it is generally overkill for smaller websites with only a few developers. But that doesn't mean aspects of MVC can't be modified to work in a more limited environment.

The above diagram is a simple representation of how the MVC pattern works. When a client (browser) requests a page from the server, the page controller will determine the actions to be taken. For example, if you want to display a catalogue of products, the controller will instruct the model to fetch the catalogue and then pass the information to the view which will display it to the user. The beauty of separating the development of the site utilising an MVC approach, is that you can use the controller to display the same content in different ways (e.g. exported as a PDF or as an RSS feed).

The downside of most implementations of MVC in PHP is that it often involves a monolithic controller -- one central location for all of the logic code. While you would generally use URL rewriting rules so you can friendly URLs (to avoid the user seeing addresses like http://www.br.net/index.php?view=list&page=2), it does make modifying the code more difficult. Monolithic controllers stem from the origin of the MVC pattern when used to structure Java applications, but it is inappropriate standard database-driven websites.

What is most often overlooked is that PHP, or rather the server it resides on, is in fact a controller itself. By calling a specific URL you can instruct the server to perform certain action. For example, if I call blog.php, I can keep all my blog-related actions in one place, separate from my links or folio. It also means my administration controllers will be separate from the rest of my controllers so I can simply password-protect the folder without worrying about implementing user-authentication (but if I had to, the separation of code and design would make it easier).

Let's say we have a simple blog site. The controller for viewing the data would look something like this:

blog.php

<?php

require('lib/Globals.php');
require('lib/DataAccess.php');
require('lib/BlogModel.php');

$da =& new DataAccess($host, $user, $pass, $name);
$blog =& new BlogModel($da);

switch ($_GET['view']) {
   case 'single':
      $blog->fetchBlog($_GET['id']);
      include('lib/Header.php');
      include('lib/BlogSingleView.php');
      include('lib/Footer.php');
   break;
   default:
      $blog->fetchAllBlogs();
      include('lib/Header.php');
      include('lib/BlogListView.php');
      include('lib/Footer.php');
   break;
}

?>

This is only a simple version of a controller, given that it doesn't doe any sort of error-checking or input validation, but you can see the general structure.

First we start by including our global variables, which contains our information for connecting to the database. If you're going to do it this way, you should make sure to change the directory permissions. The other option is store everything other than your controllers outside of the public_html directory.

After that we include any modules we may need to use. The DataAccess module is our base for interacting with the database. This is kept separate from our model so that only one connection needs to be opened for many operations. We also bring in any of the required Model code. In this case we're only using the blog model, but you would have a different model for each of the tables you need to draw from.

 Alright, now that everything is included, we start by creating our DataAccess object, which will be used as an interface by our models to interact with the database. The contents of our DataAccess.php file are as follows:

lib/DataAccess.php

<?php

class DataAccess {

   var $db;
   var $query;

   function DataAccess($host,$user,$pass,$name) {
      $this->db = mysql_pconnect($host,$user,$pass);
      mysql_select_db($name, $this->db);
   }

   function fetch($sql) {
      $this->query = mysql_query($sql, $this->db);
   }

   function numRows() {
      return mysql_num_rows($this->query);
   }

   function getRow() {
      return mysql_fetch_assoc($this->query);
   }

}

?>

For the sake of simplicity I have stripped this class d own to its bare basics, so when implementing it yourself you will want to include some error-checking in there. However even with that, it would still be a pretty simple class.

We have to properties, $db and $query. $db hold the open database resource itself, while $query holds a result set of any data retrieved from a query run on the database.

So, our constructor simply opens the connection and assigns it to the $db property, and then selects the database to work on. After that we have three methods for accessing the database: fetch, numRows, and getRow.

The fetch method simply runs whichever query is sent to it from the model and stores the results in $query property. It does not return any information to the model, however if you were to introduce error checking it would only return either true or false to signal its sucess or failure.

numRows simply returns the number of rows in the result. Again, if error checking was to take place you would return false if there was no record set stored (or there were no rows -- an invalid record set).

Finally, getRow returns the next row in the set. This means that you can only retrieve one row at a time from the database itself, but this allows mySQL to manage the information, and not PHP.

Next in our controller, we initialise the blog model, passing it our newly established Data Access object as the interface:

lib/BlogModel.php

<?php

class BlogModel {

   var $dao;

   function BlogModel($dao) {
      $this->dao =& $dao;
   }

   function fetchAllBlogs() {
      $this->dao->fetch('SELECT * FROM blog ORDER BY ID DESC');
   }

   function getRow() {
      if ($row = $this->dao->getRow()) {
         return $row;
      } else {
         return false;
      }
   }

   function numRows() {
      return $this->dao->numRows();
   }

?>

After analysing the Data Access object (DAO), the model becomes pretty straight-forward. We have a property to store a link to the DAO, a constructor which assigns it, and three methods which call the DAO to fetch some data. For the sake of simplicity I've left out the admin methods, such as INSERT, UPDATE, and DELETE but they are run in the same way as fetchAllBlogs() -- by sending SQL to the DAO.

You can also create methods to find the information in different ways, for example to only fetch the last 10 blogs, or to search by the title or unique ID. Basically, every time you need a different query to be run on the table, you create a new method for it.

So now we've gone through the guts of our code, the last thing to look at is our views. In my example, I've used Header.php and Footer.php files to keep the general structure and HTML of the site separate from the views -- these would be everything from the <html> tags and navigation to the Google Analytics code.

The views looks something like this:

 

BlogListView.php

<?php while ($row = $blog->getRow()) { ?>
<div class="section">
<h1><? echo $row['title']; ?></h1>
<?php echo $row['summary']; ?>
<p><a href="/blog/<? echo $row['id']; ?>/">Read More &gt;</a></p>
</div>
<?php } ?>

 

and

 

BlogSingleView.php

<?php $row = $blog->getRow(); ?>
<div class="section">
<h1><? echo $row['title']; ?></h1>
<?php echo $row['entry']; ?>
</div>

 

Pretty simple, yeah? Basically, what they do is use PHP itself as the template engine by only using simple iteration, assignation, or conditional statements (none in these templates). Why use a complex package like SmartyPHP when using simple statements alone can be just as easy to follow?

What the first one does is use a while statement to iteratively fetch a row from our blog model. For each row it creates new surrounding <div> tags, and outputs the information. You can see that one could just have easily used an XML structure around the data to create an RSS feed instead of outputting HTML.

In the second view, all we've done is fetch one row and output it. The Header.php and Footer.php files do the rest for laying out the page, so our views are kept to a minimum size. These two views are almost exactly the same to what's used on this site, the only difference being one line in the list view to utilise a pagination module.

And that's really it to implementing a site utilising an MVC pattern. While it doesn't strictly adhere to the design pattern, it does segregate data from code, and code from presentation, enough for it to be easily updated when it needs to be.

Just to rehash, our flow would go something like this:

User navigates to http://www.myblog.com/blog/

                v

Server rewrites URL to /blog.php?view=list

               v

Controller (blog.php) receives input and opens database connection

               v

Controller takes URL arguments to determine action

               v

Controller instructs model to fetch data

               v

Model instructs DAO to run and store query

               v

Controller calls appropriate view (list)

               v

 --> View calls model to retrieve a row

|              v

^   Model calls DAO to fetch row from mySQL

|              v

 --< View outputs row as required

               v

Browser waits for user input.