MVC in PHP Part 1: getting the feet wet
In the introduction to this series we described the purpose of the series and looked at the basic idea behind the model-view-controller pattern. Right now, we will be setting up our development environment by defining a file and folder structure. After that, we will write our first bit of code to handle a page request with a controller, process the data through a model, and display the response using the view layer.
Requirements
The title of the introductory article clearly stated that we would be taking a modern approach, so we will need up-to-date software. A short list of requirements:
- Apache 2 webserver with mod_rewrite enabled (more on that in the next paragraph)
- Support for PHP5, preferably the latest stable version.
- A MySQL 5 database which we will configure in one of the upcoming articles.
The folder structure
As said, the MVC system requires a separation of the controllers, the models and the views. By mere logic, we should at least have separate folders for each of these parts. On top of that, we will need an index file to handle all incoming requests and initiate the controller. Note that these files should be in the root directory of the server. If you want your application to work inside of a subdirectory, we will need extra configuration in our controller. We will discuss such configurations later in the series, but for now, let us work in the root directory to keep things simple. Currently, your folder structure should look like this:
/controllers /models /views index.php
In the previous paragraph we said that our page-requests would be handled by the index file. We will need a separate file to tell mod_rewrite which requests to handle by itself and which one it should redirect to the index. We will do this by creating a .htaccess file and put it in the same directory as the index file. This is what should be in the .htaccess file:
RewriteEngine on RewriteRule !(\.(css|js|jpg))$ index.php
We're not going to dig too deep in the workings of mod_rewrite and .htaccess, but the lines above might need a bit of explanation. The file contains a regular expression that tells the web server to redirected all requests index.php, except for the request ending with the extensions in the second line. We can adjust this line to add more extensions if we want our server to handle requests for certain files instead of redirecting them to the index.php. Right now, it's sufficient to exclude css (stylesheet), js (javascript) and jpg (image) files. Possible, and useful, additions to this list are png (images), gif (more images), swf (flash), ico (favicon) and mp3 (music). We will get back to this and make several additions to this list if the need arises.
Before proceeding to the next step, I advise you to test the configuration first. If you took my advice at the end of the introductory article, you should have no trouble writing some hello world code in the index file. After that, just browse to any url on your server and you should get redirected to the index file, giving you the nice little hello world you've written. Make sure to clear the index file afterwards.
Creating the controller, the model and the view files
We already created the index file and the .htacces file, it's time to start using our folder structure. We will create a file in each directory and give it a good name. I suggest something like this to start:
/controllers controller.php /models model.php /views view.php index.php .htaccess
Since we are going to use all of these files and we're only using our index file to process the requests, we need to include the files in the index file. To do this, we will use require_once since these files are vital for our application and we will only need to include them one time. I will also omit the php opening and closing tags in the code examples, assuming all files should start and end with these tags.
require_once("controllers/controller.php");
require_once("models/model.php");
require_once("views/view.php");
The first request
In this first example, we will take an incoming request and send it to the controller. The controller will then send a subsequent request to the model and send its information to the view layer which, in turn, will display the requested information. Please note that this is not the way you should write your applications, but it serves the purpose of clearly demonstrating the inner workings of a model view controller system.
The index.php file
require_once("controllers/controller.php");
require_once("models/model.php");
require_once("views/view.php");
// strip URI from trailing and leading slashes to get the request
$request = trim($_SERVER['REQUEST_URI'],'/');
// send the request to the controller
$controller = new Controller();
$controller->procesRequest($request);
On top of including the necessary files, our controller will take the current URI as the request and send it to our newly instantiated controller object. To fully understand the controller, we take a look at the code there.
The controller.php
class Controller {
public function procesRequest($request) {
$model = new Model();
$response = $model->getResponse($request);
$view = new View();
$view->setResponse($response);
$view->display();
}
}
Besides the class definition there is the publicly accessible procesRequest-method. In this method, we create both our model and view objects. We send our request to the model which, in turn, sends us the appropriate response. The controller sends this response to the view object and tells the view object to start the output. As said, the response is created by the model. The code for that model looks like this:
class Model {
public function getResponse($request) {
if ($request == "") {
$response = "there was no particular request";
} else {
$response = "the request was ".$request;
}
return $response;
}
The model receives the current request and responds accordingly. I don't think this basic if-statement needs any further explanation. Finally, we take a quick look at our basic view object:
class View {
private $response;
public function display() {
echo $this->response;
}
public function setResponse($response) {
$this->response = $response;
}
}
The view layer has a private variable that can be changed using the setResponse-method. The display-method then outputs the current response.
The aftermath and how to proceed
For starters, I'm fully aware that all of the above code is practically unusable in a serious environment. The idea here is not to have solid and high-quality code, but to gain a better understanding of how we can use the model-view-controller pattern to create web applications. For a simple application like this, the use of separate files, classes and rewriting might look completely over the top, but it allows us to further expand each part of the application as we see fit.
In the next article, we will take a look at how we can create and use configuration files which we will need to improve and expand our models, views and controllers. In the meantime you can learn a thing or two about classes and objects in PHP.
Where do we go now?
You can browse through the recent articles or go to the archive for older items.
There are 5 comments for this article
Tim on Dec 2, 2008
ltrim and rtrim? why not just trim?
Jonas on Dec 3, 2008
You're absolutely right :) Never really thought about it since I've been using this way to strip the request uri for years now. I just copied the line from some existing code i had.
I'll change the line in the article (and in my code :) )
Hasanga on Dec 19, 2008
This is grate! keep up the good work brother.
Just a one quick question, wouldn't it be nice to have separate file where you define all the paths of your folders and files(core) to a variables or to an array and use those through out your application. And it will give you a very easy manageability.
Thanks!
Jonas on Dec 22, 2008
That is indeed a good idea, and it's one of the reasons why I wanted the series to start off with an article on configuration files (check out part 2). Paths can be defined in whatever type of config-file and can be used later to check the integrity of your framework or for quick configuration-changes throughout the entire framework.
corteny on Dec 18, 2009
As good as an idea as this is all files required for configurations and quick changes have really well thought-out framework which will take time. I look forward to your creations and will try to be patient. Good Luck :)