Dewald Botha
open source web thoughts
open source web thoughts
so, i’ve been pretty much a zend framework addict, ever since i coded my first bootstrap. thinking back to that countless hours trying to understand the beast that is zf, ahhh, what fond memories… and lately i’ve also been playing around a bit with kohana, which is another web based mvc framework, but definitely a bit more lightweight and easier to use than others.
there is however a bit of an issue with web based mvc frameworks, and in my opinion a large freaking elephant that no one seems to talk about. how can i call a controller from within a view and assign an action to it – and is it correct to do so?
and before the quiet whispers start, i’d like to note that this does not necessarily go against mvc principles. if you think about it – the whole point of mvc is to seperate business logic, application logic and the view (presentation logic) – and i’ve confirmed this with a couple of conversations with other developers. but on the other hand, if you feel that i have overstepped a boundary – please do let me know if there is a better, or more correct way of doing this.
now lets give a practical problem:
i have a view template, lets call it home, which consists out of a header, navigation, content and footer. now my content is pretty much controlled by the controller and whatever the view calls to display. but say for example something you wish to partially include a login form in the top header.
how do i actually use a controller to change the way the view behaves, without building the logic into all the controllers which i access. now in order to overcome this – we can attach a controller, lets call it log controller, to the view. this controller will have the business logic to decide whether or not a login or a logout form must be generated.
now the obvious would be to include a partial view, but then the issue arrives where you let your view start making logic decisions, instead of presentation. on the other hand, you can use the current controller to help you decide this, but if you ask me, that might be a bit of overkill and lots of extra and duplicate coding.
in zend framework, i found the solution to be a bit easier than in kohana, but still fun to figure out.

okay, so i have my home template, which is controlled by my home controller. on my home template in my header i have a form which would change depending on if the user is logged in or not, which i would like to be controlled by my log controller.
so, in my view i would do the following call:
<?php echo $this->action('log','generateForm') ?>
which would go to the log controller and call the method generateForm() – so in generate form i will be able to decide whether the user is logged in or not and assign the corresponding result to the view assigned to the controller, in the case above it would most likely be the generateForm view.
there you go – without having to overcomplicate things or duplicate code, you can assign controllers as part of your presentation logic with a simple call. i have however not yet figured out how to pass variables to the action method you are calling, but as soon as i have an update i will let you know.

in kohana the task is a bit more complicated – luckily i found in the underbelly of the kohana forums an user with the same problem – which came up with the following solution -
<?php
class Dispatch_Core{
protected $controller;
public static function controller($controller)
{
$controller_file=strtolower($controller);
// Set controller class name
$controller = ucfirst($controller).'_Controller';
if(!class_exists($controller, FALSE))
{
// If the file doesn't exist, just return
if (($filepath = Kohana::find_file('controllers', $controller_file)) === FALSE)
return FALSE;
// Include the Controller file
require_once $filepath;
}
// Run system.pre_controller
Event::run('dispatch.pre_controller');
// Initialize the controller
$controller = new $controller;
// Run system.post_controller_constructor
Event::run('dispatch.post_controller_constructor');
return new Dispatch($controller);
}
public function __construct(Controller $controller)
{
$this->controller=$controller;
}
public function __get($key)
{
if($key=='controller')
{
return $this->$key;
}
else
{
return $this->controller->$key;
}
}
public function __set($key,$value)
{
$this->controller->$key=$value;
}
public function __toString()
{
return $this->render();
}
public function render()
{
return (string) $this->controller;
}
public function __call($name,$arguments=null)
{
if(method_exists($this->controller,$name))
{
return $this->method($name,$arguments);
}
return false;
}
public function method($method,$arguments=null)
{
if(!method_exists($this->controller,$method))
return false;
if (method_exists($this->controller,'_remap'))
{
// Make the arguments routed
$arguments = array($method, $arguments);
// The method becomes part of the arguments
array_unshift($arguments, $method);
// Set the method to _remap
$method = '_remap';
}
ob_start();
if(is_string($arguments))
{
$arguments=array($arguments);
}
switch(count($arguments))
{
case 1:
$result=$this->controller->$method($arguments[0]);
break;
case 2:
$result=$this->controller->$method($arguments[0], $arguments[1]);
break;
case 3:
$result=$this->controller->$method($arguments[0], $arguments[1], $arguments[2]);
break;
case 4:
$result=$this->controller->$method($arguments[0], $arguments[1], $arguments[2], $arguments[3]);
break;
default:
// Resort to using call_user_func_array for many segments
$result=call_user_func_array(array($this->controller, $method), $arguments);
break;
}
// Run system.post_controller
Event::run('dispatch.post_controller');
if($result!=NULL)
{
$result=ob_get_contents();
ob_end_clean();
}
return $result;
}
}
now the above you can save under your library directory in your application for ease of use as something like dispatch.php.
so to solve the problem of the login form – in my default home template i’ve added <?php echo $this->template->partial = new View('log/generateform');?>
in the generateform view i called i will use the following:
<?php
//call the log controller
$dispatch = Dispatch::Controller('log');
//call the generateForm method
$dispatch->method('generateForm');
//return the result from the template to the view
echo $dispatch->template;
?>
the above will call the log controller and the method generateForm, in the controller, you can specify the template, for e.g. <?php public $template = 'kohana/partial/navigation';?> and then in the action / controller assign any values to be used by the template view/presentation.
oh, and one more cool thing with the above is that you can also call the method using parameters for e.g. <?php $dispatch->generateForm(array('title'='login'));?>
as i said in the beginning, if anyone feels that i’ve overstepped the mvc divide, please let me know, i’m sure that with the assigning of a controller/action from within a view is totally acceptable, since only your presentation logic is affected by it really, and the controller still decides on what is available for the view to pull, without having adjust your controllers to cater for everything.
and if someone knows of a quicker method in kohana, also do let me know.
| Print article | This entry was posted by dewaldbotha on January 23, 2009 at 11:36 am, and is filed under framework, mvc, php. Follow any responses to this post through RSS 2.0. You can leave a response or trackback from your own site. |
about 1 year ago
Personally, I’m not one to get all anal about so called principals. IMHO, that’s okay! I cannot see it overstepping mvc principals in principal.
On a different note, a framework like Django don’t even allow you to set variables in templates, never mind calling actions in templates! See: http://code.djangoproject.com/ticket/1322 where someone requested a feature to set variables in a template. It was outright closed with a “won’t fix” reason. Yet, I consider Django one of the best frameworks around.
The important factor is that you can get it to work for you within the boundaries of the framework. As soon as you need to make changes to the core of the framework (i.e. the source maintained by the community) it hampers future upgrades and defeats the purpose of using a framework of any kind (i.e. have a community of developers maintain some of the code of your project).
$dispatch->method(generateForm’); You forgot an ‘ before generateForm.
(me being anal
)
about 1 year ago
thanks for the syntax check and feedback – (i’ll blame it on the wordpress devils) –
i agree with your point of keeping within a framework – especially if you have to make changes to some core functions for it to work for you – then you are most probably using it wrong.
i’m just contemplating the fact that i’ve never really found too much write-up on the whole issue of using the view in a framework to decide on the actual presentation logic, and i must say i quite agree with the django approach of not setting variables within a template, since it kind of defies the point of mvc -
but who knows, maybe i’ll start a whole drive and convice people otherwise in the whole action/view debacle of january 2009
about 1 year ago
Rails are heavy action driven (i.e. the view dictates the presentation logic, AFAIK), so it is quite different from Kohana/Django in that respect. Yet Rails are the acme of mvc frameworks in many people’s eye! I think the issue are not being pressed since it is not really that big a deal. As long as the basic principles are being followed, its okay.
Keep in mind that Django is considered an MVT (Model-View-Template) framework and not MVC, which does make it different.
about 1 year ago
yip – i must admit that i’ve only started reading up on django this morning, but from what i’ve seen so far it seems like a pretty competent framework compared to others such as pylons.
as for the mvc and presentation/action logic issue, i guess i just needed that bit of reassurance -
but alas, for know, i’m looking deep into the chapters of my ‘dive into python’ book
about 1 year ago
I don’t see why you couldn’t determine if the user is logged in in your controller and then check the flag in your view?
title ?>
heading ?>
is_logged_in() ): ?>
Hello, $user->name
username:
password:
footer ?>
about 1 year ago
try that again:
[html]
[head]
[title] [?= $page->title ?] [/title]
[/head]
[body]
[?= $page->heading ?]
[!-- conditionally displayed content --]
[? if ( $user->is_logged_in() ): ?]
Hello, $user->name
[? else: ?]
[form]
username: [input name="username"]
password: [input name="password"]
[/form]
[? endif ?]
[!-- common content --]
[?= $page->footer ?]
[/body]
[/html]
about 1 year ago
@Aaron You can check for a logged in flag in the view, but now, say you want to add that form to one or more views, you have to copy that code in all relevant views.
So with the above solution, you can have that piece of code in one central place and then just call < ?php echo $this->template->partial = new View(‘log/generateform’);?>, which will allow for easy an easy reusable component that can be used in multiple views.