Given the concept of ‘skinny controllers, fat models’ and the general acceptance that Views can directly call on Models when requiring data for output, should one consider handling the ‘get and display’ parts of requests within the Views and not the Controller? For example (attempted to keep code fairly generic):
Controller
<?php
class Invoice extends Base_Controller {
/**
* Get all the invoices for this month
*/
public function current_month() {
// as there's no user input let's keep the controller very skinny,
// DON'T get data from the Model here, just load the view
$this->load->view('invoice/current_month');
}
}
View
<?php
// directly retrieve current month invoices here
$invoices = $this->invoice_model->get_current_month();
// get some other display-only data, e.g. a list of users for a separate list somewhere on the page
$users = $this->user_model->get_users();
?>
<h1>This month's invoices</h1>
<ul>
<?php foreach ($invoices as $invoice) { ?>
<li><?php echo $invoice['ref']; ?></li>
<?php } ?>
</ul>
To me, this makes at least some sense in the cases where a request is essentially just a View. Why should the Controller have to collect and pass on the data to the View when it can just retrieve it itself? This leaves the Controller open for purely ‘Application level’ processing (e.g. handling GET/POST requests, managing access rights and permissions etc.) as well as keeping the Models reusable and all the other good stuff.
If this example was extended to allow a user to filter the results, the Controller would just handle the POST from the form and pass the filters to the View, which would then request the data again, this time with the filters.
Is this a valid approach to developing an MVC application? Or am I overlooking an important part of the role a Controller should play?
Yes, it can technically be done. No, it shouldn’t be done. And yes, you’re missing a bit of what the controller is there for.
The controller is there to decouple the View from the Model. The decoupling is beneficial because you should look at the View as almost throw-away code. As your UI technology changes, you want to minimize the rework required in generating a new View. The controller enables that decoupling and provides a place for your code that will live through UI technologies.
It works in reverse as well if you need to add to or change out your Model. All of the upstream changes will be contained within the Controller and your Views will be left alone.
The other risk is that while the View is very simple now, you have less guarantee that it will remain so simple throughout its life. By calling the Model directly from the (very simple) View, you’ve opened the door a bit to allow additional bad practice to creep in later when the very simple View needs to become not-so-very simple. A future developer will be tempted to make more Model calls from the not-so-very simple View instead of refactoring the code and interacting with a Controller.
7
Given the concept of ‘skinny controllers, fat models’ and the general acceptance that Views can directly call on Models when requiring data for output
No. This is not correct. View can not directly call on Models. Views should not have access to Model objects, unless for some reason the programmer has exposed those objects to the View.
should one consider handling the ‘get and display’ parts of requests within the Views and not the Controller?
That basically erases the Controller, and defeats the point of having them.
Why should the Controller have to collect and pass on the data to the View when it can just retrieve it itself?
The Controller doesn’t collect the data. The Model does the collecting of the data. The Controller decides if this data should be passed to the view. The View just does the presentation of the data.
If this example was extended to allow a user to filter the results, the Controller would just handle the POST from the form and pass the filters to the View, which would then request the data again, this time with the filters.
No.
The Controller checks if the POSTed data is valid, it then passes this data as options to the Model, which would then query the datasource and return the data, and the Controller passes that to the View.
Is this a valid approach to developing an MVC application? Or am I overlooking an important part of the role a Controller should play?
The Controller operates as a handler to requests by the browser. A dispatcher sends the request to a controller’s action, which in turn, spreads the request out to the Models. The Models contain all the business logic (this is the fat part), and give the data back to the controller. The controller can then simplify and adjust the data so that it’s easier for the View to present it.
The point of the View is to decouple the structure and dependency between the presentation of HTML and the DataSource. While this can be difficult. Views don’t always present data that came directly from a Model. The controller often adds extra data that is relevant.
I’m sure there are a lot of tutorials out there on MVC. I’d recommend reading some of them.
2
I found your question very interesting because I ran into the same issue while learning Python recently.
While the answers given make a convincing argument, I thought I would add another opinion I came across in which the View does get the Model’s state without going through the Controller.
It is important to note that both the view and the controller depend
on the model. However, the model depends on neither the view nor the
controller. This is one the key benefits of the separation. This
separation allows the model to be built and tested independent of the
visual presentation. The separation between view and controller is
secondary in many rich-client applications, and, in fact, many user
interface frameworks implement the roles as one object. In Web
applications, on the other hand, the separation between view (the
browser) and controller (the server-side components handling the HTTP
request) is very well defined.Model-View-Controller is a fundamental design pattern for the
separation of user interface logic from business logic. Unfortunately,
the popularity of the pattern has resulted in a number of faulty
descriptions. In particular, the term “controller” has been used to
mean different things in different contexts. Fortunately, the advent
of Web applications has helped resolve some of the ambiguity because
the separation between the view and the controller is so apparent.In Application Programming in Smalltalk-80: How to use
Model-View-Controller (MVC) [Burbeck92], Steve Burbeck describes two
variations of MVC: a passive model and an active model.The passive model is employed when one controller manipulates the
model exclusively. The controller modifies the model and then informs
the view that the model has changed and should be refreshed (see
Figure 2). The model in this scenario is completely independent of the
view and the controller, which means that there is no means for the
model to report changes in its state. The HTTP protocol is an example
of this. There is no simple way in the browser to get asynchronous
updates from the server. The browser displays the view and responds to
user input, but it does not detect changes in the data on the server.
Only when the user explicitly requests a refresh is the server
interrogated for changes.
I’m not in a position to say which of the opinions is “right”, and to be honest, I am a bit more confused after reading the answers here and the linked article.
Full text of article here.
2
Another thing to consider is that you appear to have autoloaded the user_model
and invoice_model
to allow the the view to access them. For this to work reliably, you probably autoload all of your models (because $this->load->model()
just looks wrong in a view, doesn’t it…)
Doing this unnecessarily bloats your stack by loading a bunch of stuff that may never get used. Part of the reason for having multiple models is to allow you to encapsulate related logic and load only what you need for a given task.
This looks like CodeIgniter. I’ve done a lot of CI development and I can share from personal experience that you really don’t want to autoload more than you really have to. Try adding $this->output->enable_profiler(TRUE);
in a controller’s constructor and fiddle with autoloads (including helpers like database
): you’ll likely see a significant change in load and execution times, but especially in memory allocation.
1
The short answer is that the form of your code sample is deceptively intuitive. It would seem that this is an “easy on the mind” way to go.
Problem #1
Your Model
and View
objects will be tightly coupled.
If you ever have to add or remove methods in the Model
, then you may have to alter the View
accordingly.
Fundamentally, MVC is derrived from the Command and Observer patterns. You want an independent ‘Model’ that is manipulated via an interface / API that the Controller
can hook into (i.e. delegation).
Frequently, this means injecting Model
and View
instances into a Controller
and storing them as a properties of said Controller
. Then, using a method of the Controller
(i.e. a command) as the working area, pass data to a View
from the Model
(after the `Model has finished updating application state).
Passing data (arrays, iterable objects, whatever) keeps coupling between Model
and View
instances loose. If you inject the Model
instance into the View
, see Problem #1 above.
Remember, Views
could be HTML, JSON, Text, XML, HTTP headers, YAML, or almost anything, following a representation state transfer methodology (REST).
Thus, the key to understanding how to manage the relationship between the Model
and Views
is to see the relationship for what it is, one-to-many (potentially)! This is exactly what the Observer pattern was designed to accomplish.
While most setups only have one view to be concerned with at a time, there is nothing that stops the MVC architectural pattern from updating multiple views at one time! Working with traditional CRUD web applications makes people think in a one-to-one way, but that is the smallest example of how the Observer pattern could work (one-to-many being the other).
Thus, if you had one Model
and multiple Views
, the potential headache of updating all the Views'
implementation code because you changed something in the Model's
API / methods now becomes acute.
Pass data to Views
, not instances of Models
.