37

I have to seem problems grasping the concept of Views in MVC, they are, according to what I've read, the layer that manages the presentation in the aplication, but many of the material I've been reading seem to be different regarding this matter in this one from PHP Master.com.

The View is a class with functions that return some HTML code, where is the rest of my HTML? should it be put in independent .html pages that access this View code?

In this article, from php-html.net the View is a simple HTML file with a .php extension, but how are they accessing that data? I see no require() or anything like the instantiations in the first tutorial.

Matt Busche
  • 14,216
  • 5
  • 36
  • 61
Eduardo Naveda
  • 1,880
  • 3
  • 15
  • 30
  • 1
    Is you asking abstractly or in reference to a framework? – Jason McCreary May 16 '13 at 18:37
  • No frameworks, just trying to understand how people would do it without a framework. – Eduardo Naveda May 16 '13 at 18:40
  • 1
    Look into this chapter of the Symfony framework's documentation: http://symfony.com/doc/current/book/from_flat_php_to_symfony2.html It takes you from a spaghetti app to the MVC pattern, explaining steps along the way. – Denis de Bernardy May 16 '13 at 19:11
  • 1
    Maybe the ideal concept of MVC is not as good as the "poor" MVC-cloned frameworks. It's like GOTO. Conceptually it is seen as poor design; but is used in the Linux kernel code ... because it works fine. – Marcos May 16 '13 at 19:32
  • 1
    Indeed, these are "for kids" or "for dummies" - but knowing they do not encarnate the **pure original MVC essential spirit**, I think there is no harm in using these tools as intended, to get some things done... – J. Bruni May 16 '13 at 19:40

7 Answers7

83

Note: the MVC and MVC-inspired patterns are advanced constructs. They are meant to be used in codebases where ordinary object-oriented (that follows SOLID and other guidelines) code starts to become unmanageable. By introducing this pattern you would impose additional constraints, which then lets you to contain very complex applications. MVC is not meant for "hello world" apps.


Let's start from the beginning ...

The core idea behind MVC and MVC-inspired design patterns is Separation of Concerns. Said separation is two-fold:

  • model layer is separate from UI layer:
  • views are separated from controllers

enter image description here

Model layer (not "class" or "object") would contain several groups of structures, each dealing with as different aspect of business logic. The major parts would be:

  • domain objects: validation, business rules
  • storage abstraction: persistence and caching of data from domain objects
  • services: application logic

Also there might be mixed in repositories, units of work and others.

UI layer mostly consists of views and controllers. But they both utilize services to interact with the model layer. Services provide the way for controllers to change the state of model layer and for the views to gather information based on that new state.

In context of web the views and controllers form a loose pair, because of the request-response nature that web applications exhibit.

It should be noted that although controllers can alter the state of the current view directly, it's more common that these changes are effected through the model. One reason to alter the view directly is, for example, when instead of XML you need to respond with JSON.

Though it also could be argued that one could simple instantiate a different view for each output format and take advantage of polymorphism.


What is not view?

There is a widespread misconception that views are simply glorified template file. This mistake became extremely popular after release of RubyOnRails prototyping framework.

Views are not templates. If you use them as such, you break the core principle behind MVC and MVC-inspired patterns.

If you pretend that templates are views, it has an enormous impact on your architecture. There is no place for presentation logic in the view, therefore you push the presentation logic either in controller or model layer. The usual choice is "controller", because most of people understand that presentation logic has no place in model layer.

Essentially, this causes a merger of views and controllers.


What is view doing?

The responsibility of the view is to deal with presentation logic. In context of web the goal for view is to produce a response to the user (which, btw, is the browser not the human).

Technically it would be possible to create client side views, that user web sockets to observe model layer, but in practice it's virtually impossible to implement. Especially not in PHP environment.

To create this response view acquires information from model layer and, based on gathered data, either assembles response by distributing data to templates and rendering or sometimes simple sending a HTTP location header.

When using Post/Redirect/Get, the redirect part is performed by the view, not the controller as often people tend to do.


Highly subjective bit:

Lately I have preferred to interact with MVC using following approach:

  // the factory for services was injected in constructors
  $controller->{ $method.$command }($request);
  $response = $view->{ $command }();
  $response->send();

The $method is the current REQUEST_METHOD, that has been adjusted fake a REST-like API, and the $command is what people usually call "action". The controller has separate routines for GET and POST (an other) requests. This helps to avoid having same if in every "action".

And on the view I call similarly named method, that prepares a response that is sent to the client.

Warning:I suspect that this setup contains an SRP violation. Adopting it as your own might be a bad idea.


What about DRY?

As you might have noticed already, there is a slight problem with having views as instances. You would end up with repeating pieces of code. For example: menu or pagination.

Lets look at pagination .. The pagination contains logic, but this logic is not related to the model layer. The model has no concept of "page". Instead this bit of logic would reside in the UI layer. But if each of your views contains or inherits pagination, then it would be a clear violation of SRP (and actually several other principles too).

To avoid this issue you can (and should, IMHO) introduce presentation objects in your views.

Note: while Fowler calls them "presentation models", I think that name just adds to the whole 'what is model' confusion. Therefore I would recommend to call them "presentation objects" instead.

The presentation objects deal with repeated pieces of logic. This makes the views much "lighter", and in some aspects starts to mirror the structure of services from the model layer.

The interaction between presentation objects and templates becomes similar to the interaction between domain objects and data mappers.


Do I always need all of this?

No. This specific approach is heavily geared towards code, where the UI layer has a lot of complexity and you need to separate the handling of input from presentation just to sane sane.

If your application has very simple UI, like .. emm .. you are making REST API for a larger integrated project. In such the pragmatic option can be to just merge every controller-view pair into single class.

It also can be a good step, when refactoring a legacy codebase, because this less-constrained approach lets you move entire chunks of old code. When you have isolated such pieces of older code and checked, that everything still works (since legacy code never has any tests .. that's how it becomes "legacy"), you then can start splitting it up further, while focusing on separating business logic from UI.


P.S. I myself am still struggling with figuring out a way how best to deal with views. This post is less of an answer and more like a snapshot of my current understanding.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • 1
    I just stumbled upon this, while searching for a way to maintain the single responsibility principle in my views (i.e. produce a list of users, table of products, render forms, etc...). Presentation objects would take reoccurring elements such as pagination, search form, etc... out of the view, allowing re-usability in many views. I think this is an elegant solution, instead of loading multiple views per page for every UI element. +1 – Dean Aug 10 '15 at 15:57
  • Do you know any articles on this technique in PHP perhaps? – Dean Aug 10 '15 at 16:29
  • Nope. Only article that I have found regarding this was by Martin Fowler: http://martinfowler.com/eaaDev/PresentationModel.html – tereško Aug 10 '15 at 16:48
  • So what's the difference between 'Presentation Object' and 'View Model' ? – smoothware May 04 '18 at 20:45
  • 1
    @smoothware the point of "ViewModel" in MVVM (as I understand it) is to isolate the presentation from the business logic. In a classical MVC the view would observe the model layer for changes (or in Model2MVC/WebMVC, it would just pull everything due to request-response life cycle). But in MVVM the View is observing the ViewModel, while ViewModel is observing Model layer. Effectively it create a pattern, that is useful, when you have no control over the code in either UI or business logic, because the ViewModel acts as an "adapter" in between. – tereško May 04 '18 at 20:53
  • @tereško Thanks for your reply. That was about my understanding of the MVVM, and after reading the article by Martin Fowler, I found it hard to see the difference, e.g. it says: "A view then simply projects the state of the presentation model onto the glass." I feel like this sentence could just as well go "A view then simply projects the state of the ViewModel onto the glass." – smoothware May 06 '18 at 09:15
  • 1
    @smoothware that's not it. The *presentation models* (I really prefer to calling them "presentation objects", since Fowler's habit of adding "model" to everything causes some confusion) are actually using the data, that View has retrieved. And they isolate some some part of presentation logic. On a website a good example would be the main navigation (there is always this fiddly bit of logic for choosing the "current active item" and maybe some user-level constraints) . That is a purely UI logic, but you wouldn't want to repeat that code in every View you have. – tereško May 06 '18 at 10:08
  • You could say that the relation of "view - presentation object - template" is a bit similar to "service - entity - datamapper" . – tereško May 06 '18 at 10:11
11

The second tutorial is the way that Code Igniter framework works, and the one which I am used to. I follow it even when using no framework at all.

In fact, the developer must put the MVC-like principles in practice, otherwise he/she can make the lasagne or spaghetti even using the most MVC-like oriented framework.

Using the "PHP file as view template" approach, ideally one would use minimum PHP statements, basically only for repeat-structures (foreach ($array as $item)), basic conditionals (if ($boolean)) and echo - as if it was, indeed, a template language, and nothing more.

So, the <?php ?> tags in the view template files should be merely placeholders, and nothing else.

No database queries, access to the model, calculations, and the like should be performed in the view template file. It should be treated, basically, as an HTML file with placeholders. (With its associated CSS and JavaScript. Things may got more complex when the application relies on JavaScript / AJAX a lot...)

Following these simple principles, we effectively do some separation of the presentation from the business logic. Even sounding so simple, I'm tired of dealing with Code Igniter code which does not follow it. Some use "helper functions" to disguise the model/database calls - and think it is a good practice! :-)

You don't see a require inside these PHP view template files because they are required instead, from the "view building" methods.

Of course, one should not echo and/or print from inside the controller and model functions. This is also very simple, but I am also tired to see spaghetti code echoing out HTML from inside CI controller methods.

In practice, the controller calls the model methods, build all the necessary data for the view, and as a last step, calls the view (i.e., builds and output it), passing the already previously obtained data to it.

Makes sense? I don't know if I answered your question. At least, these are my "2 cents".

J. Bruni
  • 20,322
  • 12
  • 75
  • 92
  • 2
    @tereško: ok, I've changed "MVC" to "MVC-like" to satisfy the purists. In fact, I've read your answers to other questions, and learned I should say "MVP" because what they name "Controller" is in fact a "Presenter"! – J. Bruni May 16 '13 at 19:28
  • 1
    @tereško: I've read also your 254 upvotes (by now) answer; you're a true professional! – J. Bruni May 16 '13 at 19:35
1

You are supposed to pass a method in the view class everything it needs to build a view independent of the output format. Most developers will use some sort of templating engine to build the bulk of the page then fill in the body with request specific information. There are so many ways you can go about doing this. It is also good to have an abstract view class that defines helper methods for common elements like forms and inputs.

This layer is abstracted so that the logic of your application doesn't have to change if you decide to change the design or output format in any way.

Edit: Each module represented by an MVC set would have its own view that has a collection of methods responsible for sending your output the the browser. There are many ways you can go but here is one example:

class testModule_view extends viewAbstract {
    public function showTestData($title, $subtitle, $data) {
        $XHTML = '<h1>' . $title . '</h1>'
            . '<h2>' . $subtitle . '</h2>'
            . parent::data2table($data);

        parent::outputToBrowser(DEFAULT_TEMPLATE, $XHTML);
    }
}

This is just a quick example to give you an idea of what a simple view method might look like.

km6zla
  • 4,787
  • 2
  • 29
  • 51
  • A method? let me see if I get it, in the View class a method like "OUTPUT", and this method renders the entire html needed? I imagine that's a simple way of doing this, and that's exactly what I want, understand the simplest form of it so I can go on and try a framework of my choice with litle effort. – Eduardo Naveda May 16 '13 at 18:42
  • 1
    What I don't like about this is that `

    ` is hard-coded inside of PHP. And the parameters are now tightly coupled to the presentation.

    – Dave Jarvis May 16 '13 at 21:02
  • @DaveJarvis I would like to reiterate that "This is just a quick example to give you an idea of what a simple view method might look like." @Eddnav usualyl you will want to replace hardcoded tags as Dave suggested with methods like `->pageTitle()` and `->pageSubtitle()`. This was not meant to be a full view, just a small representation of one. – km6zla May 16 '13 at 21:07
  • 1
    @ogc-nick: That still doesn't alleviate the problem, it only shifts the hard-coding from `showTestData` to the other methods. If you wanted to completely change the appearance of the title (e.g., use a `DIV` instead of `H1`, or even generate a desktop app using XUL), this approach is insufficient. If you want to port the code to another language, all the UI work will have to be rewritten, which is wasteful. There are other approaches that decouple the view (presentation) from the underlying implementation. – Dave Jarvis May 16 '13 at 21:37
  • 1
    @ogc-nick: For example, what if you wanted to create a PDF from the output instead of rendering it as a web page? Or allow the same data be queried by a third-party? You'll have to create new code for the different views of the data, which runs counter to the underlying principles of MVC. In other words, you shouldn't have to touch the PHP code to create a PDF from the same content. – Dave Jarvis May 16 '13 at 21:43
1

Different frameworks use different logic to assign variables to view and get its content. Below is a simple example using ob_start() function.

<?php
     $title = 'Hello...';
     ob_start();
     file_get_contents('view.phtml');
     $viewContents = ob_get_clean();
     echo $viewContents;

?>

//view.phtml
<b>Hello the title is <?php echo $title; ?></b>

Hope this answer your question...

Jay Bhatt
  • 5,601
  • 5
  • 40
  • 62
  • 7
    That's not a view. It's a [template](http://codeangel.org/articles/simple-php-template-engine.html). – tereško May 16 '13 at 22:31
0
<?php

class View {

protected $data;

protected $path;

protected static function getDefaultViewPath() {
    $router = App::getRouter();

    if(!$router){
        return false;
    }

    $controller_path = $router->getController();
    $method_path = ($router->getMethodPrefix() !== "" ? $router->getMethodPrefix() . '_' : '') . $router->getAction();

    return ROOT . "/views/" . $controller_path . "/" . $method_path . ".phtml";
}

public function __construct($data = array(), $path = null) {

    if(!$path){
        //default
       $path = $this->getDefaultViewPath();
    }

    if(!file_exists($path)){
        throw new Exception("Error view file!");
    }

    $this->data = $data;
    $this->path = $path;
}


public function render(){
    $data = $this->data;

    ob_start();
    include ($this->path);
    $content = ob_get_clean();

    return $content;
}

}

David
  • 11
  • 1
0

Check this code:

include_once(ROOT.'/'.'config/config.php');

function __autoload($class_name){
    $lib_path = ROOT . '/' . 'lib/class.'.$class_name . '.php';
    $controller_path = ROOT . '/' . 'controllers/'.str_replace("controller", "", strtolower($class_name)) . '.controller.php';
    $model_path = ROOT . '/' . 'models/'.strtolower($class_name) . '.php';

if(file_exists($lib_path)){
    require_once ($lib_path);
} else if (file_exists($controller_path)){
    require_once ($controller_path);
} else if(file_exists($model_path)){
    require_once ($model_path);
} else {
    throw new Exception("File {$class_name} cannot be found!");
}

}
Jefferson
  • 794
  • 10
  • 24
David
  • 11
  • 1
-1

When the browser calls a page, a controller will be loaded for this one. The controller manages the lifecycle of your app. He'll grab data from the model, which is only used to get data (maybe from a database). The View is only HTML, the controller will echo the view, and if neccessary, pass a few parameters to it.

hice3000
  • 186
  • 3
  • 12