MVC (Model-View-Controller) in PHP tutorial part 1: Hello World
Update 20/02/2019 - I have created an updated version of this article which implements an immutable version of this code. See: Immutable MVC: MVC In PHP 2019 Edition. This article still contains the relevant concepts but you may wish to read the update after finishing this article.
Update 05/08/2015 - Removed View/Controller coupling, I'll explain why this is in another article as it has led to some confusion for which I apologise.
Model-View-Controller
A basic implementation demonstration of the Model-View-Controller (MVC) architecture in PHP.
Since writing my article Model-View-Confusion part 1: The View gets its own data from the Model I have received several e-mails asking me to provide a simple example of MVC in PHP. So I've decided to create one. However, my intent is to build up from a very simplistic example and include some of the reasoning behind certain design decisions and considerations involved in deploying MVC on the web.
As a quick recap, most MVC implementations work by having the controller do all the work. The controller instantates the view. The controller calls methods on the model. The controller takes data from the model and then passes it to the view then the controller handles the output from the view. Controller, Controller, Controller! PHP Developers are controller mad!
Not only is this not MVC, it's a very inefficinet method of programming. This article gives a bare-bones purely MVC article adhering to the pattern in the way it was designed as strictly as possible. The result of this is a lot less work in the controller.
As I also stated in my previous article, a lot of other tutorials/articles go off into irrelevant sub-topics. I want to cover pure MVC. As such, you'll find no discussion of template engines, directory structures, DAO, ORMs or similar in this article. This is pure MVC. From the ground up I'll explain the original implementation of MVC and why it has to be adapted to work on the web.
What is the MVC Pattern?
This is a broad definition of the components as defined by the pattern. Later on I will describe variants of this but this is MVC as described by the original implementations in Smalltalk-80. Where variations are made, I have highlighted them.
The Model
In its simplest form the model stores data which is to be accessed by the view and written to by the controller.
The model is the most complex of all the parts of the system and will contain all the logic which is specific to the application and where domain entities that relate to real world concepts (such as "a user" or "an order") are stored. It is the part of the application which takes data (from any source) and processes it. The model also handles all data access and storage. It has no knowledge of any controllers or views which may use it.
For example, in PHP the model may represent a "User" in the system. It will handle any operations regarding users. Saving/loading records, validating registrations.
The model is not (common mistakes made by those misinterpreting the pattern):
- A simple data access point
- A class called "model" which represents a single database table
The View
The view contains all the display logic. In PHP it will be the part of the application which generates the HTML. It has direct access to the Model and can query the model to get its data. The View can create callbacks to its controller (for example a clicking a button in the view would trigger an action in the controller). A lot of MVC examples state that the view is decoupled from everything else and fed data by the controller. This is entirely inaccruate (see: Model-View-Confusion part 1: The View gets its own data from the Model for a detailed explanation). In MVC the vews queries the model to request its own data.
The View is not (common mistakes made by those misinterpreting the pattern):
- Absent of logic
- Given data by the controller
The Controller
The controller takes user input and updates the model where required. Where there is no user interaction (e.g. where a static data set is displayed and will be the same every time), no controller should be necessary. It is important to note that the controller is not a mediator or gateway between the view and the model. The view gets its own data from its model. The controller accesses the model but does not contain any display logic itself. All the controller does is respond to user input.
It's important to note that the controller is not in charge of instantiating the model or the view. The controller knows of them but flexibility is heavily reduced in implementations that force the controller to select which view and model is being used.
Each controller is linked to a single instance of a view and a single instance of a model.
The Controller is not (common mistakes made by those misinterpreting the pattern):
- A gateway/mediator between the model and the view
- The place where views get initialised based on the state of a model. The controller is linked to a single view class (although it could be assigned to multiple instances) and responds to actions on it. For example a list of products would be a single view. The controller would handle user actions directly relating to that view such as sorting the list, filtering the records it's displaying based on criteria specified by users. However, the same model/view/controller triad would not also deal with displaying the infoformation about a single product based on a given ID. This is a different responsiblity and as such requires a different view, model and controller.
Program flow
The typical program flow in MVC is:
- The model, view and controller are initialised
- The view is displayed to the user, reading data from the model
- The user interacts with the view (e.g. presses a button) which calls a specified controller action
- The controller updates the model in some way
- The view is refreshed (fetching the updated data from the model)
Hello World
In standard MVC, the view would be initiated, assigned a controller and model, then rendered (Burbeck, 1992; Krasner & Pope; 1988). The user would interact with the view and this would trigger actions on the controller.
A hello world application would look something like this:
class Model {
public $text;
public function __construct() {
$this->text = 'Hello world!';
}
}
class View {
private $model;
public function __construct(Model $model) {
$this->model = $model;
}
public function output() {
return '<h1>' . $this->model->text .'</h1>';
}
}
class Controller {
private $model;
public function __construct(Model $model) {
$this->model = $model;
}
}
//initiate the triad
$model = new Model();
//It is important that the controller and the view share the model
$controller = new Controller($model);
$view = new View($model);
echo $view->output();
Notice how the controller isn't doing anything? This is because there is no user interaction. Hello world is purely display.
N.b. the controller is passed to the view because in a desktop application, the view would need to create a callback on the correct controller, so it would need to know which controller was in use. This still happens on the web, but it a more subtle way. In Part 2 I'll show how this is worked around on the web.
The controller does not create the view
In a lot of (so called) MVC examples and frameworks, the controller acts as an entry point that creates its view and model. I'll explain why this is later on, but it's important to understand that this is not something defined by the pattern itself and using it as an entry point can imply the usage of "fat controllers" (Brady, 2009)) whereby the controller is acting as a gateway between the view and model. See my article Model-View-Confusion part 1: Why the model is accessed by the view in MVC for more information on why this is not good practice.
In this case, the traditional approach works as intended on the web. However, when user actions need to be defined, a problem occurs that forces us as PHP developers to deviate from this standard approach.
User interaction
This wouldn't be a complete example if one of the components (in this case the controller) wasn't doing anything. So let's add some user interaction so that the controller has something to do.
User interaction is the first problem with the MVC architecture on the web. There's no way to create a callback to the controller directly. The whole page needs to be reloaded. Still, not a huge issue but there needs to be some code to route the action back to the (correct!) controller.
As a simple example I will extend the Hello World example and make it so that when "Hello world" is clicked, the text will be changed to "Text updated".
Firstly, add a link to the view. This can be likened to adding a callback to the controller from the view. In GUI applications, the controller would just be another object in memory and the callback would be a simple function call. This is not an option on the web the only way to access the controller is another HTTP request.
class View {
private $model;
public function __construct(Model $model) {
$this->model = $model;
}
public function output() {
return '<a href="mvc.php?action=textclicked">' . $this->model->text . '</a>';
}
}
Routing
On the callback (second request) all the components must be re-initialised. Some code must be added to route the action back to the controller:
$model = new Model();
//It is important that the controller and the view share the model
$controller = new Controller($model);
$view = new View($model);
if (isset($_GET['action'])) $controller->{$_GET['action']}();
echo $view->output();
All this does is call the relevant controller action (specified by $_GET['action']) based on the user interaction.
Finally, a controller action to handle the event has to be added:
class Controller {
private $model;
public function __construct(Model $model) {
$this->model = $model;
}
public function textClicked() {
$this->model->text = 'Text Updated';
}
}
Now, when the page is visited it will say "Hello World" when the link is clicked it will say "Text updated". Not exactly rocket science but it's a basic working example of pure MVC in php.
Click here to see it working or click here to see the full source code.
Is that it?
Yes. There you have a simple bare bones example of MVC, albeit with some slight modifications to deal with the underlying technology (the web). However, deploying the MVC pattern on the web presents some other significant technical challenges which I will address in part 2.
It is important to understand the basics of the pattern to be able to appreciate or even see the design decisions and concessions which have to be made to make it work correctly on the chosen platform. A lot of MVC architecture tutorials skip this step and tend to throw you in at the deep end and include implementations of platform specific design decisions without explaining what is done because it's specified by the pattern and what has been done to work around limitations of the underlying technology, all the while usually only presenting how and not why.
I've tried to keep a clear distinction. In part two I'll expand on this and show other problems introduced by deploying the MVC architecture on the web. And in part three I will show you how to create a fully functional router from scratch.
Update: February 2019: I have created an updated version of this article which implements an immutable version of this code. See: Immutable MVC: MVC In PHP 2019 Edition. This article still contains the relevant concepts but you may wish to read the update after finishing this article
References
Brady, P (2009) Zend Framework: Surviving the deep end (http://www.survivethedeepend.com/zendframeworkbook/en/1.0/
the.model#zfbook.the.model.the.fat.stupid.ugly.controller)
Burbeck, S (1992) Applications Programming in Smalltalk-80(TM): How to use Model-View-Controller (MVC) (http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html)
Deacon, J (2000) Model-View-Controller (MVC) Architecture (http://www.jdl.co.uk/briefings/MVC.pdf)
Krasner & Pope (1988) A Description of the Model-View-Controller User Interface Paradigm in the Smalltalk-80 System (http://www.itu.dk/courses/VOP/E2005/VOP2005E/8_mvc_krasner_and_pope.pdf)
Sun Microsystems (2000) Java Blueprints Model View Controller (http://java.sun.com/blueprints/patterns/MVC-detailed.html)