11

My current implementation:

class SomeController extends AppController
{
    function someaction()
    {   
        $d['text'] = "ahoy!";
        $this->render("someactionView", $d);
    }
}

And in AppController:

function render($file, $data = "")
{
    require "views/" . $file . ".php";
}

And the $data will be available in the views file. Is this a correct implementation? Are there any fallacies with this implementation?

good_evening
  • 21,085
  • 65
  • 193
  • 298
  • If a `Controller` is responsible for drawing / requiring HTML (as in your example), then the controller has encroached on the responsibility of the `View`. If a view can have presentation logic (say, loops for drawing a table), then that logic should be in the view, which could be in a method of a class. – Anthony Rutledge Mar 05 '17 at 02:10

3 Answers3

13

And the $data will be available in the views file. Is this a correct implementation? Are there any fallacies with this implementation?

Basically you do implement it like the most frameworks do. There's a couple of problems with that:

  • A controller takes an input and sends an output (which breaks the Single-Responsibility Principle)
  • A view is tightly coupled to HTML. Because of this, you cannot re-use the same view for another stuff, like XML, JSON.
  • If you do require "views/" . $file . ".php"; in render() method - you again tighly couple it. What if you change the location of views? Then you would have to slightly rewrite your method. This approach merely kills reuse-ability.

To refresh your basic knowledge:

Controller (also known as Editor)

Serves only singular purpose. It changes model state - that is, it should take an input that comes from $_POST, $_GET, $_FILES, $_COOKIE. In controller only variable assignment should be done and nothing more.

class Controller
{
   public function indexAction()
   {
        $this->view->setVar('age', $this->request->getPostParam('age'));
        $this->view->setVar('user', $this->request->getPostParam('user'));
        //...
   }
}

View

A view has a direct access to a model. In order to make make views more re-usable and maintainable you'd better pass required things as function parameters (or via setters)

class View
{
   public function render($templateFile, array $vars = array())
   {
      ob_start();
      extract($vars);
      require($templateFile);

      return ob_get_clean();
   }
}

How the view should be initialized and how the variables should be passed to it?

First of all - a view should be instantiated outside MVC-triad. Since a controller writes either to view or model - you'd pass variables to view via controller.

$model = new Model();
$view = new View($model);

$controller = new Controller($view);

// This will assign variables to view
$controller->indexAction();

echo $view->render();

Note : In real world scenario, a model isn't a class, but abstraction layer. I call it Model for demonstration purposes.

Yang
  • 8,580
  • 8
  • 33
  • 58
  • 2
    Nice to see some updates here. Can you explain how the view communicates with the model in your example? How is the component called which initializes the view, model, and controller? – hek2mgl Sep 24 '13 at 21:21
  • 1
    @hek2mgl 1) A view communicates with a model directly - either via `factories/service locators` 2) It's called `Front Controller`. There're several components its made of (Router, Dispatcher, Response sender). The thing is : it manages all requests via single place. You can see its overall implementation in `Zend Framework/library/Mvc`. As for a good start, I'd recommend this http://r.je/mvc-in-php.html – Yang Sep 24 '13 at 21:57
  • First, currently I'm more a shell script coder. Last time I've code web I had: Router which creates the controller based on request, The controller(presenter, I've learned) then communicates with the model and passes results to the view. The view encapsulates functionality like template rendering, translation, dynamic javascript. I've read about that MVP, that's exactly what I mean. Initialization of model and view where coded in that levels but the presenter was responsible for creation of them – hek2mgl Sep 24 '13 at 22:34
  • I still don't see where the view communicates with the model *in your example*. You just passing `$vars` from the controller, like me calling `assign()`. Also you are passing a template name like me. Where is the difference (*in your example*) ? The difference that I see, is that you are calling `render()` from the `FrontController` .. That looks interesting.. – hek2mgl Sep 24 '13 at 22:37
  • Yes, exactly a presenter is responsible for managing models and views. Also note a presenter **should not** output anything. Instead it should return a string (be it HTML or plain text), then an output should be done via `echo`, like `echo $presenter->indexAction()`. Otherwise you end up with code duplication and less flexibility. – Yang Sep 24 '13 at 23:25
  • Here's the direct communication `$view = new View($model);` in the example above – Yang Sep 24 '13 at 23:26
  • 1
    @DaveJust , it should be `$controller = new Controller($model, $view);` unless there are several presentations of the same model and in one particular case user has no ability to affect the model layer. – tereško Sep 26 '13 at 02:13
3

IMO the render() method belongs to the view and not to the controller. The code should look like this:

Controller:

class SomeController extends AppController
{
    function someaction()
    {   
        $d['text'] = "ahoy!";
        $view = new SomeActionView();
        $view->assign('data', $d);
        echo $view->render();
    }
}

View Base Class:

class View
{

    protected $data;

    function render($template) {
        ob_start();
        // you can access $this->data in template
        require "views/" . $template . ".php";
        $str = ob_get_contents();
        ob_end_clean();
        return $str;
    }


    function assign($key, $val) {
        $this->data[$key] = $val;
    }
}

Extend View class

class SomeActionView extends View
{

    public function render($template = 'someActionTemplate') {
        return parent::render($template);
    }

}
hek2mgl
  • 152,036
  • 28
  • 249
  • 266
  • I think *your* attempt is wrong but haven't downvoted it. Although some literature suggests that the view can communicate with the model I would not do so. Also I think there are multiple ways to skin a cat. – hek2mgl Sep 23 '13 at 02:03
  • Yeah , the "some literature" is made by people like Fowler, Burbeck, Evans and Cunningham. And your literature is made by 37signals. – tereško Sep 23 '13 at 02:59
  • I just kept things simple as in the question. Of course template loading will have it's own logic. – hek2mgl Sep 24 '13 at 23:54
2

Is this a correct implementation? Are there any fallacies with this implementation?

Short answer: no and several.

First of all, what you have there is no a view. It's just a dumb php template. Views in MVC are instance, that contain the UI logic for the application. They pull information from model layer and, based on information they receive, create a response. This response can be simple text, JSON document, a HTML page assembled from multiple templates or simply a HTTP header.

As for controller, it's only task is to alter the state of model layer and (on rare occasions) the current view. Controllers do not initialize the views nor do the populate templates.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • Then how the view should be initialized and how the variables should be passed to it? – good_evening Sep 23 '13 at 01:06
  • I would not confirm to this. If you are not populating the data from the controller to the view, the view isn't independent anymore and you have a strong coupling between the view and the model. – hek2mgl Sep 23 '13 at 01:06
  • 2
    @evening , the view would be instantiated outside the controller and passed in as a dependency. And data for view would not be passed in, instead the view would call services from model layer and get all the data that it needs. This also would mean that views are made for specific interface of the model layer. – tereško Sep 23 '13 at 01:49
  • @tereško that's a highly questionable design. It can be done but as I said, this gives you a strong coupling between view and model. – hek2mgl Sep 23 '13 at 02:08
  • 1
    Please go and read what "tight coupling" actually is and what causes is, because you obviously have no clue what you are talking about. – tereško Sep 23 '13 at 03:01
  • Sorry that my english is not perfect as yours. I know what I mean, and for me a view should know nothing about the model and the controller. That way it is perfect for testing, reusable and lightweight. call it like you want, I call it zero coupled. – hek2mgl Sep 23 '13 at 03:50
  • Do you not get it?! Your "way" does not have a view. You have only templates, that, instead of being managed by a view, are handled by controller, which has assumed the responsibilities of view. – tereško Sep 23 '13 at 04:55
  • don't you see that I just wanted to show OP that `render()` does not belong to the controller? While views in real life are more than plain templates, a template wrapper is a good starting point to show how things could work. Also I just modifiyed the OP code instead of introducing something new. And I'm still the opinion that in web applications a view should not communicate directly with the model. If you want to make such access happen use something like data bindings. But this is far beyond this question – hek2mgl Sep 23 '13 at 12:00
  • But however thanks for pointing me towards some interesting literature. I always appreciate this. – hek2mgl Sep 23 '13 at 12:09
  • @hek2mgl Tight coupling basically is about global state (including static classes, signletons and global variables) – Yang Sep 24 '13 at 02:39
  • @hek2mgl Also, if view has no clue about models and controller, then you're implementing a **Model-View-Presenter** (This is what most frameworks call "MVC", including CodeIginter, CakePHP, YII...) – Yang Sep 24 '13 at 02:41
  • @DaveJust That MVP sounds more suitable for web applications. Didn't know the name before. I had a system where the views where able to access the model, some time ago. I've thrown this away. Now presentations are dump slaves and the code is much easier to debug, test and maintain. – hek2mgl Sep 24 '13 at 21:23
  • @DaveJust Thanks for clarification about "tight coupling". May I ask how is it called when a component (view) cannot being used independently from another component (model)? Is it just a dependency? – hek2mgl Sep 24 '13 at 21:35
  • @hek2mgl People don't use MVP because a `Presenter` (that takes user input and handles UI logic) violates the **Single-Responsibility Principles** because it serve two tasks. As for the second question - No, its not a "dependency", its much more - its `Separation of Concerns`. – Yang Sep 24 '13 at 21:51
  • @DaveJust Your third examples enlights me.. However will you both give +1 – hek2mgl Sep 24 '13 at 23:42