When developing a website using Model-View-Controller pattern, there is a risk of misunderstanding the role of controllers, views and models. This results in making the controllers huge and models small, which in turn makes it difficult to test and support your application. This section's goal is to give you a general understanding of what code may be placed in a controller class, what code may be placed in a view template, and what code may be placed in a model class.
The idea behind the term "skinny controller" is that typically, in your controller classes, you put only the code that:
$_GET
, $_POST
, $_FILES
and other PHP variables);ViewModel
variable container.A controller class should avoid:
For an example of a "skinny" controller, look at the CurrencyConverterController
class below. This controller provides the "convert" action method whose goal is to convert
an amount of money from EUR to USD currency. The user passes the amount of money through the
"amount" GET variable.
class CurrencyConverterController extends AbstractActionController
{
// Currency converter model
private $currencyConverter;
// Constructor. It's purpose is to "inject" dependencies.
public function __construct($currencyConverter)
{
$this->currencyConverter = $currencyConverter;
}
// The "convert" action displays the converted money amount
public function convertAction()
{
// Get the money amount from GET
$amount = (float)$this->params()->fromQuery('amount', -1);
// Validate input data
if($amount<0) {
// Money amount is missing
$this->getResponse()->setStatusCode(404);
return;
}
// Pass the data to the CurrencyConverter model
$convertedAmount = $this->currencyConverter->convertEURtoUSD($amount);
return new ViewModel([
'amount'=>$amount,
'convertedAmount'=>$convertedAmount
]);
}
}
The controller's action method above does the following:
Takes the data passed by site user (line 16). This data is usually part of
Request
object and can be retrieved using the controller's getRequest()
method
or Params
controller plugin.
Performs the basic check on the data passed by user (line 19), and if the data is missing (or invalid), sets an HTTP error code (line 21).
Passes the money amount to the CurrencyConverter
model (line 26) by calling its convertEURtoUSD()
method. The method
then returns the converted amount.
Constructs the ViewModel
variable container and passes the resulting
data to it (line 28). This variable container can be further accessed in the corresponding
view template responsible for data presentation.
Because you need to keep your controllers as thin as possible, most of the business logic of your application should be put into model classes. In a properly designed Model-View-Controller application, models look "huge". A model class may contain the code which:
Performs complex data filtering and validation. Because the data that you retrieved in controller is passed to your application from an outside world, in your model, you have to take a lot of effort to verify the data and ensure the data will not break your system. This results in a secure website resistant to hacker attacks.
Performs data manipulation. Your models should manipulate the data: e.g. load the data from database, save it to database and transform the data. Models are the right place for storing database queries, file reading and writing functionality, and so on.
In a model class you are not recommended to:
Access the data from the HTTP request, $_GET
, $_POST
and other PHP variables. It is the controller's
work to extract that data and pass it to model's input.
Produce HTML or other code specific to presentation. The presentational code may vary depending on the user request, and it is better to put it in a view template.
If you follow these principles, you will encounter that your models are easy to test, because they have clearly identified input and output. You can write a unit test which passes some test data to input end of the model, retrieves the output data and verifies that the data is correct.
If you are confused whether to put certain code in a controller or in a model, ask yourself: is this an important business logic that needs to be carefully tested? If the answer is yes, you should put the code in a model.
Because most of the logic is stored in models, your view templates should be as simple as possible to produce the presentation of the data passed through the variable container. In a view template, you may:
Keep static HTML markup code.
Retrieve the data from a variable container and echo them to PHP output stream.
If a controller passed a certain model through a variable container, poll the model for data (e.g. you can retrieve table rows from a database table and render them).
Contain simple PHP flow control operations, like if
, foreach
, switch
and so on. This
allows to vary the presentation depending on variables passed by the controller.
The view template is not recommended to:
Access data from the HTTP request and super global PHP variables.
Create models, manipulate them and modify the state of the application.
If you follow these principles, you will encounter that your views can easily be substituted without modifying the business logic of your application. For example, you can easily change the design of your web pages, or even introduce changeable themes.