An alternative to requestAction

Published on and tagged with cakephp  controller

While reading Felix’ article “requestAction considered harmful” I asked myself: “Hey, why don’t we just instantiate the respective controller and call the action directly?”

And that’s what I did. Here is the result:

public function index() {
    App::import('Controller', 'Example');
    $example = new ExampleController();
    $example->constructClasses();
    $foo = $example->getFoo();
    // do something with $foo
}

The only “difficulty” is that you have to “manually” load the models and components used by the respective controller with constructClasses().

It seems to work fine and it is faster than requestAction, but it is possible that some callback methods are not called (at least the shutdown() callback of components is not called). So, be careful with this approach. And think twice before using this approach or requestAction ;-)

27 comments baked

  • Martin Westin

    Your posts are usually interesting but this time you must have read my mind.

    I was just stuck on some requestAction() issue this morning.
    And I always feel like I am cheating when I use it :)

    This might be an alternative for me since I use requestAction as an event notifier between controllers (i.e. I usually don’t need Components or even views for these requests).

  • Daniel

    Why not keep the data logic in the model and just load the model? That should be cleaner.

  • Martin Bavio

    I dont like this approach at all, it´s way harder to mantain, and it feels just like cheating. There has to be better ways.

  • cakebaker

    @all: Thanks for your comments!

    @Martin Westin: I hope this approach is helpful in your scenario.

    @Daniel: Yes, I agree with you. Usually there is no need to use requestAction, especially if you follow the “fat models, skinny controllers” principle. But if someone has to use requestAction for whatever reason, then the approach I described above could be a faster alternative.

    @Martin Bavio: Follow the “fat models, skinny controllers” principle, and you probably never have to use requestAction or what I described here ;-)

  • rafaelbandeira3

    blah… you cheated DHO!!! :P

    I would really like to read your point on my comment in Felix’s post : http://debuggable.com/posts/requestaction-considered-harmful

    []’s

  • Brendon Kozlowski

    If the “fat models, skinny controllers” principle will help to “probably” never have to use requestAction, how can one retrieve data from models to display on pages that use the Pages controller? Is there a way to load info without pushing anything app-wide ever needed into the pages controller itself?

  • Kim Biesbjerg

    I did some benchmarking requestAction vs. my alternative requestAction method. You can find the results and the code in the bin: http://bin.cakephp.org/saved/36360

  • rafaelbandeira3

    @Kim Biesbjerg : now, that was dirty!

    @Brendon Kozlowski : using fat models you can use on your controller’s loadModel()… but interacting with data that is just needed in the view layer, is the great mistery of CakePHP’s MVC lock! lol

  • Martin Bavio

    Daniel, I do follow Fat Model Skinny Controllers principle, but it does NOTHING to do with the way I use requestAction…

    Suppose I have a navbar with different elements, so I use element to load them in the layout. Now, inside elements I have requestAction calls, and there is where my problem is. Is there any other way of setting this navbar?

  • Daniel

    @Martin Bavio: What about using your app controller’s beforeFilter to always load that data from a model and set it for the views if it’s going to be used on every page?

  • rafaelbandeira3

    @Daniel & @Martin Bravio : Components, someone? Something like a WidgetComponent… $this->Widget->load(‘ThisWidget’, ‘ThatWidget’, ‘TopCommenters’, ‘OverkillWeekTip’);

  • Martin Bavio

    @Daniel: what if it is info from several models?

    @rafael: I already mentioned Mini-Components by AD7six. I was just trying to learn if there is another cooler way.

  • Martin Bavio
  • rafaelbandeira3

    @Martin Bavio : sorry Martin, but what I suggest has nothing to do with Mini-Controllers suggested by Andy Dawson. I actually did a sort of proposal, never tested or did something like, just spited out maybe you could end up with a great idea.

    @Martin Bavio : First you could abuse of Model relations ( using $this->Post->Comment->findMostRateds() ), or you could use $this->loadModel(‘Comment’); and then access the Model via $this->{$ModelName}… or you could just instantiate it via ClassRegistry::init(‘Comment’, ‘Model’); … anyway we got a bunch of approaches, but all of them seem wrong and dirty…

  • Tarique Sani

    Ummm…. where in MVC is it stated that a View cannot access a Model directly to present data?

    Yes, you cannot / should not modify the state of a model from within a view so no update or save should be done – but that I believe is very obvious. That approach to keep Model completely detached from View is true for the MVP pattern.

    So what Rafael has done is AFAIK correct! @Rafael – when are you releasing the code? ;)

    Which brings me to the point – should there be ability to load helpers in the views?

  • rafaelbandeira3

    @Tarique Sani : are you answering felix’s post comment? If yes, it wasn’t already been made as a releasable code… we modeled out a API and took something like 40min to make everything run as it was supposed, but there’s a lot of things to implement and to take care… at least for this specific project wich is heavily acl based, in a very particular scheme – wich I might release some day, so of course there are lots and lots of things to consider before doing this in a really acurrate manner, even because nobody wants to mess with one of the coolest things CakePHP gave us : clean views. And we all know… throw it to the noobies, proclaiming they’ll earn some nifty performance enhancements, and CakePHP’s community will crack on messed up sql’ed htmls.

    @Tarique Sani : what do you mean? shouldn’t helpers be loaded in views? I do spend a lot of time in the view layer – man I’m so tired of writing HTML – and Helpers actually do everything for me, they inflect, localize, format, count, wrap, embed… They keep everything clean, easy to mantain and self-explainable… i.e. ($view is ViewHelper – wich mixes InflectorHelper, LocalizeHelper, FormatsHelper, HtmlHelper, DataHelper)

    echo $view->modelDisplayField(‘Request’);
    echo $view->modelDisplayField(‘Company’, array(‘fieldName’ => ‘Featured Client’));
    echo $view->modelDisplayField(‘Owner’, array(‘fieldName’ => ‘Mandatory Owner’, ‘valueOptions’ => array(‘link’ => ‘/profile/%s’)));
    echo $view->modelData(‘Request’, array(‘blacklist’ => array(‘primaryKey’, ‘foreignKeys’, ‘displayField’)))

    Is documentation needed for this? And where are the +100 html lines repeating classes and formatting, generating ids and wrapping links?

  • Tarique Sani

    @Rafael – If I have use a particular helper I have to do define them in the $helpers array of the controller – I would like to be able to do this in the view directly.

  • rafaelbandeira3

    @Tarique Sani : ok, you have a point… but remember that all that is going to be used in the view – at least in the template – is in the knowing domain of your controller’s action, so it’s pretty logical to let it define wich helpers should be used or not. As in your view you will be inside of your controller’s action domain, there’s no reason to have a set of include like instantiations…
    //views/modules/posts/view.ctp
    loadHelper(‘Rss’); // how would the var be extracted?
    // or
    echo $this->Js->object($data); // too big…
    ?>

    Is it worthy?

  • rafaelbandeira3

    the code was cutted off … :(

    //views/modules/posts/view.ctp
    <?php
    $view = new ViewHelper(); // what about the inner Helpers?
    $js = new JavascriptHelper();
    $html = new HtmlHelper();
    //or
    $this->loadHelper('Rss', 'Html', 'Javascript'); // how would the var be extracted?
    // or
    echo $this->Js->object($data); // too big...
    ?>
  • Tarique Sani

    @Rafael – oh! at this stage I am not worried about the implementation, just that it would be a worthy idea to investigate.

    IMO actions per-se need not be aware of what *kind* of view (RSS, JSON, PDF) is being rendered hence they need not bother about which helpers to load.

  • rafaelbandeira3

    @Tarique Sani : me neither… i’m worried about my clean views! :)

    Ok, I think data controller’s actions shouldn’t behave different independently of the content type being requested as you said, but your idea seems just too abstract for me… I can’t see a good reason for that… not even a logical reason, as I presented a logical reason for controllers do that…

  • Tarique Sani

    @Rafael – I prefer my controller to be skinny, clean and easy to follow, the whole point of OOP is abstraction.

    Where do we define which Behaviours a model uses – in the model
    Where do we define which Components a controller uses – in the controller

    Do controllers use helpers? No! Views use helpers! QED

  • rafaelbandeira3

    @Tarique Sani : your’re right again. Now, not about being strictly logical them, but following K.I.S.S. … isn’t it nice to just have things being done on your view? like
    MODEL – data /
    CONTROLLER – setup /
    VIEW – display /

    I got your point, and it’s really considerably interesting, I just don’t think it’s necessary/clean/simple… :)

    I use TimeHelper outside views…

  • cakebaker

    @all: Thanks for your comments!

    @Brendon: Hm, if you have to use model data in views rendered by the PagesController you should consider to use a separate controller and then push the data with the set method to the views. The PagesController is meant for rendering static pages.

    @Kim: Wow, quite a performance difference! You should open an enhancement ticket ;-)

    @Daniel: Yes, that’s the approach I usually use when I need some data in all views.

    @Martin: Yes, teknoid’s approach looks very interesting.

    @Rafael, Tarique: I agree with you that it is a bit illogical to define helpers in the controller but to use them in the views. At the moment I don’t see a better solution than creating them in the view as shown by Rafael. But I have to think more about it ;-)

  • rafaelbandeira3

    @cakebaker, Martin : didn’t like Teknoid’s approach. For sure it’s creative and new, but it envolves many layers of the app, sounds too dirty and not very simple.

    @cakebaker : but you noticed that I didn’t like the approach of loading models in the view template? Controller has the beforeRender method, if you mind about logic, you could set your helpers there, after all, it’s totally connected with the rendering process… seems more logical, no?

  • An idea for loading helpers in the view - cakebaker

    [...] the comments to my previous article, “An alternative to requestAction“, it was mentioned that it is illogical to define helpers in the controller but to use them [...]

  • cakebaker

    @Rafael: I think teknoid’s approach is an interesting proof of concept, but personally I wouldn’t use it. It is, as you say, not very simple…

    Yes, setting the helpers in the beforeRender() method would be a more logical alternative. But in principle I think the definition of the helpers should happen where the helpers are actually used, see also my latest article.

Bake a comment




(for code please use <code>...</code> [no escaping necessary])

© daniel hofstetter. Licensed under a Creative Commons License