Reducing requestAction() use in your CakePHP sites with fat models
When bakers first start using CakePHP there is a tendency to use requestAction()
more often than it should be. Often requestAction()
gets used to pull in common elements like recent posts or new comments, or to make menus. This makes sense in a way as it keeps the comments code in the CommentsController
and all is well. However, you start to hit a snag when your awesome application starts getting traffic. For all that is good about requestAction()
, it has one disadvantage, its slow really slow. If you have never looked at what requestAction()
is doing, it basically hits your site with another page request, minus the bootstrapping. And while you can negate much of this overhead by using caching effectively, using requestAction can lead to bad practices.
A better way
Personally I think that almost every where that requestAction
is used the following is a more useful and efficient approach. There are still cases where requestAction()
is needed but they are rare. This idea builds on the fat model, skinny controller approach in that it pairs fat model functions to elements to be used throughout your application. A quick example would be
- /**
- * Get the most Recent Comments
- *
- * @param int $limit The number of comments you want
- * @return Array
- **/
- public function recent($limit = 6) {
- $this->recursive = 1;
- 'limit' => $limit, 'order' => 'Comment.created DESC'));
- return $comments;
- }
This simply pulls up the most recent comments and returns them. If you are crafty with your model relations you should be able to easily get to your Comment
model from anywhere in your application, failing relationships you can always use $uses
to access the model you need. We then ‘pair’ this model function with an element. And by pair, they work well together but are not tightly coupled as the MVC structure in CakePHP keeps it loose.
Lets see some results
Now that we have a model function and know how to get at it we can call it in our controller.
- $comments = $this->Post->Comment->recent();
- $this->set('recentComments', $comments);
In our view wherever we want to show our recent comments we can simply include our element, which just so happens is expecting a list of comments.
Our element looks something like this.
Take note that we specify both the controller and the action in the url array, this will help keep our links pointing to the right place. This technique of model-element pairs will help lighten your controllers and reduce the amount of requestAction()
’s that get used in your application. Another advantage of adopting a fat model / skinny controller approach is that it forces you to push more code into the model, this benefits you the developer as you create more reusable code.
Mmmm but then you are tied to set all the variables that you need for a (for example) navbar in all the actions of all the controllers!
I agree with the Fat Models Skinny Controllers advice, but I dont see the point here.
I think the real solution to avoid using requestAction is AD7six Mini-Components.
Cheers,
mbavio
anonymous user on 8/15/08
we need to get lots of these little elements in the pages controller for certain pages, and I didn’t really want to load up $uses in this controller, but maybe it is the best idea… anyone?
anonymous user on 8/16/08
brian: You don’t necessarily have to load up
$uses
you can always use your model relationships to get to the data you need. This may not be the best approach for including lots of elements on the built-in PagesController. In that circumstance requestAction() might be easier.mark story on 8/22/08
were your feeds moved? my reader wasn’t updated since last two posts… anyway… I think that’s the time to rethink a lot on data sharing and non-app-bussines-logic data access on cakephp apps, because this topic was the most blogged this week and was very commented and discussed in debbugable.com, cakebaker.42dh.com, teknoid.wordpress.com, and it was blogged before. Many approaches were shown, but nobody feels comfortable about using them, because there’s no core development team position on the issue, and people don’t wan’t to make unsupported code… I think you as a cakephp member, could try to bring a solution to the community, or at least approve an approach.
Andy’s approach is the most popular, but the post itself says it’s not a clean approach.
It’s not just a philosophical discussion, it would be very good for newbies to start baking without things they will only know as harmful when they really RTFM and go around skilled blogs as the ones i mentioned before – we all know they don’t do that.
anonymous user on 8/23/08
maybe I wasn’t very clear, but I meant that every discussion about requestAction and fatModelsSkinnyControllers end up in a real world approach, where one view use bits of data from a not-related-to-the-main-data source – widgets for example. Now you can read and actually understand the comment above.
anonymous user on 8/23/08
rafaelbandeira3: Keep in mind that both Andy, and Felix are on the core team and are probably as or more qualified to give an ‘authoritative’ direction. But with this particular case I don’t think there is single right solution. Both Felix’s and Andy’s solutions are excellent alternatives to my approach and the standard requestAction. Kim’s alternative method also is a promising approach.
At the end of the day there is no right way, and it is up to each developer to choose the method that works best for him/her in each case.
I don’t think my feeds have moved. They should still be at http://mark-story.com/posts/rss
mark story on 8/23/08
mark, I know Andy and Felix are part of the core team, and notice that Felix didn’t show any solution, he just defended fatModels[…], what’s nice because the word is more spreaded around now. Kim’s solution is a bit hacky as Teknoid’s one…
But after all you answered it well, there wont be a default/prefered way, and that’s it, and that’s ok, actually that was my doubt. I think I’ll post my solution too, but first I need to get rid of all dirty tricks O’ve put there ;-)
anonymous user on 8/25/08
Thanks dude,
It helps to make custom queries
http://blog.decentmart.com
anonymous user on 8/27/08
i use some request action and use elemnt to and implement fat model skinny controller and using cake 1.x
is there any diferent ??
anonymous user on 10/9/08
Hi Mark,
I’m having trouble following your example and understanding which code goes where.
Let’s say on my home page of my website, I want data from multiple controllers (ie events, posts, comments). I understand that I have to create an element for each, and most beginners (including myself) are using requestAction within their elements.
So where do I write functions to grab data from the different controllers? I’m really lost!
anonymous user on 10/16/08
Would it just be easier to setup some basic functions in app/app_controller.php?
I find this makes life much easier for grabbing data from multiple models wherever I need it.
anonymous user on 11/18/08
Greatings,
Ugh, I liked!
Thanks
Tania
Tania on 2/4/09
Mark,
Yup, I tried passing the results variable from the controller to the elements, however this presented me with a trivial problem; it’s hard to implement AJAX views this way. So I opted for the requestAction approach; separate controller actions which can render AJAX views. The result is I can requestAction the initial page display (to make them search engine crawlable), and make these actions easily available when a user requests them by AJAX.
O.J. on 4/6/09
O.J.: That is a situation where I think that requestAction makes sense. Also I’ve since done some benchmarks on requestAction, and it turns out its not as slow as I originally thought. I still feel there are advantages in moving code that handles data into the model beyond reducing requestAction.
mark story on 4/7/09
usually i would agree
but here is a case where the “element gets model data” approach might be justified:
lets say we have lots of different elements which are cached up to 30 days once they have their information from the database and haven’t been changed since
which your approach you would get the DB content every page request for lets say 2-4 of thouse elements
only for the view to relize that the element is still cached and does not need this information
so i was thinking:
instead of those many unneccessary queries you could just use ClassRegistry::init(‘Model’)->getNavigation() inside the element
it will only be invoked if needed (changes made -> cache deleted) and would save me even some controller work
all you have to do is to set $this->element() in the view
the fat model approach still is important, though (no manual SQL or whatever)
and – if you use this only in elements – you still have some kind of structure. you could see them as some kind of mini-component – which would probably be slightly faster than any component-model-view or controller-model-view method, as well..
so what do you think about it? is it really that bad in this specific situation? any other approach? i know its against MVC, but how else could we avoid those queries if not needed in most of the page visits? and not using requestAction as it runs through the whole dispatcher again?
euromark on 8/4/09
euromark: With your example of a 30 day cache, is going to the controller really that big of a hit? I think you’ll find that
requestAction()
isn’t as slow as we all think, and there are marginal performance hits in using it sparingly.My only issue with a
ClassRegistry::init()
call in the view elements is it opens the slippery slope of how much is too much? Even with your example your rendering layer knows all about the methods and return types of the models. In addition if usinggetNavigation()
isn’t an issue, why not use find()? These are the types of questions you avoid by sticking with usingrequestAction()
, or manually passing all the needed variables into the view from the controller. Sure there are times where wrapping a model call in a controller method seems a bit over the top, but it helps you keep separation, and insulates your layers from each other.mark story on 8/4/09
Dude, this stuff is cool but I need to combine my registration and login views together. It works on separate views but when I combine it (i.e. put both views in one) it throws missing view error.
newBaker on 3/15/10
You can always use ‘$this->loadModel’ if you don’t want to load up your models with $uses.
Rafa Fuentes on 3/30/10
Usually i try to not use requestAction at all. In last 5 project i don’t even used once.
“You can always use ‘$this->loadModel’ if you don’t want to load up your models with $uses.”
But why not use it?
Drukarz on 6/17/11
I am very greatful to you mark to share such a needful information. great !!!!!
Naveen on 7/15/11