10

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?

Adam Westbrook
  • 243
  • 2
  • 8

5 Answers5

17

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.

  • 1
    Great answer, thank you. Extending your 'looking ahead' scenario slightly; if there is common information on a page that is separate to what is requested (e.g. user is viewing a specific product, a general list of 'latest special offers' are shown at the side) how/where should the call to the `offers_model->get_latest()` be made? Adding this into every method in the controller (as I've foolishly attempted before) seems like overkill and distinctly un-DRY. – Adam Westbrook Jan 31 '13 at 19:02
  • 2
    @AdamWestbrook Take a look at MVVM. The ViewModel part of that can address this specific problem. You can add the `offers_model->get_latest()` to a `ProductViewModel` base class or something similar. – Zachary Yates Jan 31 '13 at 19:05
  • 1
    Great, I'll definitely look into MVVM, thanks again. – Adam Westbrook Jan 31 '13 at 19:14
  • Very good answer, will defiantly keep this starred. Personally I'm also a big fan of MVVM :) – Benjamin Gruenbaum Jan 31 '13 at 21:44
  • @BenjaminGruenbaum Are you using MVVM in PHP? If so are you using a particular framework for it? – Adam Westbrook Feb 01 '13 at 10:42
  • I'll be honest here and say that I don't believe PHP is a very good choice for a project that is big enough to require MVVM. That said if you're looking for a MC* framework for PHP I've heard nothing but good things about CakePHP – Benjamin Gruenbaum Feb 01 '13 at 13:54
  • CakePHP is excellent, though CodeIgniter is also excellent, I would use the former to quickly build CMS products and the latter to build more unusual things. CakePHP does more for you out of the box but this is somewhat geared to towards more common, standard web apps, Code Igniter is more skeletal so you would do more to build a CMS – Toni Leigh Mar 23 '14 at 12:12
6

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.

Reactgular
  • 13,040
  • 4
  • 48
  • 81
  • Thanks Mathew. For clarification, up until now I've always decoupled the View and Model with the Controller as read and suggested. However, since starting to read up on keeping 'skinny' Controllers I've just been wondering what should/could be moved out of them, seems the thought process that lead me to this question was a step or two too far! – Adam Westbrook Jan 31 '13 at 19:06
  • When you start to get Models used by many controllers. The need for them to be fat becomes very clear. When the View starts to contain a lot of PHP then you know your controller is to thin. When your controllers are very fat. It's difficult to get other controllers to operate the same way (for example, adding an API service). – Reactgular Jan 31 '13 at 19:18
3

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.

MVC

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.

MVC - Passive Model

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.

alnafie
  • 281
  • 1
  • 2
  • 6
  • Right, and the other thing that adds confusion is client-server, which the original SmallTalk MVC did not really account for. In client-server (e.g. with javascript) there are presentation-oriented models, views, and controllers on the client, and domain-oriented views and controllers on the server, though the server also does some presentation-oriented processing adding confusion. Also, sometimes we want a domain views to have some persistence, which means the view parameters form their own model, that is not necessarily part of the domain model. – Erik Eidt Feb 02 '13 at 17:54
  • Thank you for the link, I knew I wasn't mad in thinking this! This is essentially what I'd been going off before I took the idea a bit too far, as long as the Model isn't dependant on anything what does it matter how/where it is accessed? I haven't decided what approach I'm going to take on my next development yet, but this definitely helps. – Adam Westbrook Feb 04 '13 at 12:06
1

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.

msanford
  • 750
  • 1
  • 7
  • 22
  • 1
    Good points, you're right this is based on CI, though I removed some of the specific syntax for clarity. I've got into the habit of 'autoloading' pretty much everything for largely time and DRY reasons, seemed a bit mad to have a lot of the same `load->model` in most controllers and methods. Not using a proper autoload function is one of the things I most dislike about CI's backwards compatibility, but that's a whole other discussion... – Adam Westbrook Feb 01 '13 at 10:40
0

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 Modeland View instances into a Controllerand 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.