0

I am creating a basic MVC structured CMS in PHP as a means to learn how MVC works (thus the reason I am not using a true prebuilt engine). I have a basic version working that is in structure very similar to this tutorial here. I'd like however for the views to be loaded automatically, bypassing the need for a template class. If this is strongly recommended I'll stick with the template concept (if someone could explain WHY it's so necessary I would greatly appreciate it.) Anyways, below is my router class which I have modified to automatically load the view files along the controller.

    public function loader() {
        /*** check the route ***/
        $this->getPath();

        /*** if the file is not there diaf ***/
        if (is_readable($this->controller_path) == false) {
            $this->controller_path = $this->path.'/controller/error404.php';
            $this->action_path = $this->path.'/view/error404.php';
        }

        /*** include the path files ***/
        include $this->controller_path;
        include $this->action_path;

        /*** a new controller class instance ***/
        $class = $this->controller . 'Controller';
        $controller = new $class($this->registry);

        /*** check if the action is callable ***/
        if (is_callable(array($controller, $this->action)) == false) {
            $action = 'index';
        } else {
            $action = $this->action;
        }
        $controller->$action();
    }

    /**
    *
    * @get the controller
    *
    * @access private
    *
    * @return void
    *
    */
    private function getPath() {

        /*** get the route from the url ***/
        $route = (empty($_GET['rt'])) ? '' : $_GET['rt'];

        if (empty($route)) {
            $route = 'index';
        } else {
            /*** get the parts of the route ***/
            // mywebsite.com/controller/action
            // mywebsite.com/blog/hello
            $parts = explode('/', $route);
            $this->controller = $parts[0];
            if(isset( $parts[1])) {
                $this->action = $parts[1];
            }
        }
        if( ! $this->controller ) { $this->controller = 'index'; }
        if( ! $this->action ) { $this->action = 'index'; }

        /*** set the file path ***/
        $this->controller_path = $this->path .'/controller/'. $this->controller . '.php';
        $this->action_path = $this->path .'/view/'. $this->controller . '/'. $this->action . '.php';
    }

This is preventing my view files from loading the variables given by the controller (the tutorial website has a better demonstration of this) but when setting $this->registry->template->blog_heading = 'This is the blog Index'; the view doesn't load it because the template.class is bypassed. Basically what I'm asking is how do shift the template.class over into the loading functions?

Keiran Lovett
  • 606
  • 2
  • 10
  • 28
  • 1
    Selecting a view template in the routing class like you're doing it is a bad idea. Not only you're assuming every action would always render a view but also that the view can only have one template. A separate view object with the template and variables assigned from action's level is a better solution. – lafor Jul 24 '12 at 08:55

3 Answers3

3

The quite common misconception of "view is just a dumb template" is mostly perpetuated by Ruby-on-Rails and ones that follow their broken ORM-Template-Adapter. I cannot with straight face refer to what they implement as Model-View-Controller ...

Views are supposed to handle the presentation logic in MVC and MVC-inspired design patterns patterns. Which make them objects, with ability to juggle multiple templates.

Depending on which MVC-inspired pattern you use for your web application (it is impossible to implement classical MVC with PHP) your Views would either receive data from Controller-like structures (in MVP and MVVM patterns) or be capable of requesting the information directly from model layer (Model2 MVC and HMVC patterns). I personally prefer active views, which get data from model layer.

P.S. Code like this $this->registry->template->blog_heading makes Demeter bleed.

P.P.S. For how to implement pure php templates, read this article.

tereško
  • 58,060
  • 25
  • 98
  • 150
  • The links you sent were very informative and on track with my ultimate goal. For the php template link, the tutorial called "$view->render('main.php');" how would I be able to set up my system to do render automatically, instead of manually inputting each time? – Keiran Lovett Jul 24 '12 at 11:42
  • You could could mimic that by implementing [`__toString()`](http://www.php.net/manual/en/language.oop5.magic.php#object.tostring) method for the object, then you can just use `echo $view;` and it triggers that method. But I would suggest to avoid it. It is much better to have verbose code, then to add hidden magic. Also, part of the reason for that link was to show you, that what you have been taught as "view" is actually a **template**. This will cause the presentation logic to leak into the controllers. – tereško Jul 24 '12 at 13:16
  • Ok, thanks! I guess as this is a truer form of MVC logic then I should stick to it. Thanks again for pointing out the template concept. Cheers! – Keiran Lovett Jul 24 '12 at 13:28
  • You also might find [this comment](http://stackoverflow.com/a/5864000/727208) useful. – tereško Jul 24 '12 at 13:30
1

In my own MVC I've developed, loading a view works similar to what you have, but in a much simpler way than the example you've linked to provides. (I looked at that example when I first decided to try to learn MVC, and I remember it confused the heck out of me x;.

Essentially, after you've determined that the file exists, you simply want to require (Yes require, I feel file not found is a good reason to stop execution of the script in this case) the file.

So.. instead of the whole template class thing (and I hope I'm not dodging your question and getting too far off base, here's kind of a simple example of having a controller open view files.

<?php
class Pizza_Shop_Controller extends Base_Controller
{
    public function index()
    {
        $data['users'] = array('bob','lisa','bertha');
        $data['some_string'] = "Whoa I'm a string!";

        $this->render_view('index',$data);
    }

    public function contact()
    {
        if($_POST)
        {
            Contact::process($_POST);
            return $this->render_view('contact_success');
        }
        else
        {
            return $this->render_view('contact_form');
        }
    }


}

class Base_Controller
{

    protected function render_view($view_name,$data = array())
    {
        /*
         * I also think render_view should take care of loading the layout, and then inject the content into the middle of the layout file,
         * so that you aren't trapping yourself to a specific layout, and repeating the header and footer inside of every view file
        */

        extract($data); //places all $data variables into the local scope.. very clean and ezy ;].
        require($this->root_directory.DS."$view_name.php");
    }

    /**********************************/
    public function _no_action($view_name) //Called if there is no corresponding action
    {
        /* You can use method_exists to test if a method exists within the controller, if it does not exist,
         * you can then call this function, and pass it the name of the view that is attempting to be opened
         */
         if($this->view_exists($view_name))
         {
            $this->render_view($view_name,$data);
         }
         else
         {
            $this->render404();
         }
    }

}
Anther
  • 1,834
  • 12
  • 13
  • Although that solves the problem with the variables, I think OP wants it so the views are automatically loaded depending on the controller action. I have something like this at home that I'll post when I get back but I guess it'll be answered by then haha – andy Jul 24 '12 at 08:49
  • Haha, alright. I've added a method called _no_action to signify that it's called if there is no explicit action corresponding to the view file. – Anther Jul 24 '12 at 08:54
  • That works but the only thing I would say you would need to put that in the part that calls the controller. That way you don't have to keep repeating that code if you have 50+ controllers haha. Other than that it works! – andy Jul 24 '12 at 08:55
  • True that, it should probably be part of the parent controller and _no_action should just simply correspond to a special action that the controller should do independent of normal "load a file if there's no action" action. action action action. – Anther Jul 24 '12 at 09:01
1

I know this isn't incredibly helpful to you right now, but I had the same issue a few months ago. This is based on the framework I built:

https://github.com/andyhmltn/Cherry-Framework-Blog-Example/

I'm not exactly sure where or how it does it as I haven't actually looked at it in a while, but take a poke around and the code that loads the controller, sets the variables and then loads the view is probably in the library folder. It allows you to do this:

/** Controller **/
class ExampleController extends Controller {
    public function index() {
        $helloworld = 'Hello world';
        $this->set('hello_world', $helloworld);
        #Renders view automatically
    }
}

/** View **/
echo $hello_world;
andy
  • 2,369
  • 2
  • 31
  • 50
  • I'll take a look, hopefully it's what I'm looking for. – Keiran Lovett Jul 24 '12 at 08:59
  • What you need is defiantly in there, it's just a case of adapting it to suit your own framework as I'm sure that one behaves very differently. – andy Jul 24 '12 at 09:01
  • Found where it sets the variables and then decides the variables to render: https://github.com/andyhmltn/Cherry-Framework/blob/master/library/template.class.php – andy Jul 24 '12 at 09:02
  • This looks like what I need. I just need to restructure my template file like in yours then correct? Nothing else? – Keiran Lovett Jul 24 '12 at 09:17
  • By the looks of it yes. That file just expands the variables and then loads the template file – andy Jul 24 '12 at 09:41
  • So it acts as a registry class as well then? – Keiran Lovett Jul 24 '12 at 09:43
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/14332/discussion-between-keiran-lovett-and-andy) – Keiran Lovett Jul 24 '12 at 09:44