How to correctly pass data from controller to view?

24,609

Solution 1

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.

Solution 2

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);
    }

}

Solution 3

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.

Share:
24,609

Related videos on Youtube

good_evening
Author by

good_evening

Updated on September 25, 2020

Comments

  • good_evening
    good_evening over 3 years

    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
    good_evening over 10 years
    Then how the view should be initialized and how the variables should be passed to it?
  • hek2mgl
    hek2mgl over 10 years
    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.
  • tereško
    tereško over 10 years
    @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.
  • hek2mgl
    hek2mgl over 10 years
    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
    hek2mgl over 10 years
    @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.
  • tereško
    tereško over 10 years
    Yeah , the "some literature" is made by people like Fowler, Burbeck, Evans and Cunningham. And your literature is made by 37signals.
  • tereško
    tereško over 10 years
    Please go and read what "tight coupling" actually is and what causes is, because you obviously have no clue what you are talking about.
  • hek2mgl
    hek2mgl over 10 years
    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.
  • tereško
    tereško over 10 years
    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.
  • hek2mgl
    hek2mgl over 10 years
    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
    hek2mgl over 10 years
    But however thanks for pointing me towards some interesting literature. I always appreciate this.
  • Yang
    Yang over 10 years
    @hek2mgl Tight coupling basically is about global state (including static classes, signletons and global variables)
  • Yang
    Yang over 10 years
    @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...)
  • hek2mgl
    hek2mgl over 10 years
    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
    hek2mgl over 10 years
    @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
    hek2mgl over 10 years
    @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?
  • Yang
    Yang over 10 years
    @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
    Yang over 10 years
    @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 r.je/mvc-in-php.html
  • hek2mgl
    hek2mgl over 10 years
    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
    hek2mgl over 10 years
    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..
  • Yang
    Yang over 10 years
    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
    Yang over 10 years
    Here's the direct communication $view = new View($model); in the example above
  • hek2mgl
    hek2mgl over 10 years
    @DaveJust Your third examples enlights me.. However will you both give +1
  • hek2mgl
    hek2mgl over 10 years
    I just kept things simple as in the question. Of course template loading will have it's own logic.
  • tereško
    tereško over 10 years
    @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.