The widely unused and powerful setAction()

This article is over a year old and may contain outdated information.

It has been quite a while since I last developed in Cake, but earlier today I spent a good 5+ hours working on some new controller internals for one of my apps. While working on this app, I thought I would give the not so used method, setAction() a spin. If you are unfamiliar with setAction(), it's a method that will internally forward one action to another. This is incredibly useful as it allows you to switch actions without having to do another HTTP request, nor having to reload all the controller and related objects; could be a huge saver on overhead.

Here's a quick example of a regular action:

public function profile($id) {
	$user = $this->User->findById($id);
	if (empty($user)) {
		$this->redirect(array('action' => 'listing'));
	}
	$this->set('user', $user);
}

What the action is doing is relatively simple. First it grabs a user based on an ID, checks to see if the result is empty, if so redirects to the users listing page, else sets the data. You could either redirect the action like I have, or you can simply throw an error message in the view saying something like "No user was found with that ID". I personally prefer redirecting in most cases, but it's a matter of preference. Lets move forward and try setAction() now.

public function profile($id) {
	$user = $this->User->findById($id);
	if (empty($user)) {
		return $this->setAction('listing');
	}
	$this->set('user', $user);
}

As you can see, I simply switched the redirect() with setAction(). Now if we visit a profile with an non-existent user, the listing action will be rendered without having to do another HTTP request. Also, do notice the use of return; this is extremely important because if you do not use return, all the logic after the setAction() will be ran and processed, causing unneeded overhead and unexpected results.

One downside that I have ran into with this approach is that the URL in the address bar does not change. But 99% of the time it won't matter as the page you render in its place will contain links to the correct pages or the user won't be copy and pasting the URL anyways.

8 Comments

  • Excellent... got bitten by the 'use return, or you'll get unexpected results..' and there was a 'doh!' moment when I found your blog post.

    Many thanks!

    We're using it to provide a two stage validate, then confirm save - which allows us to pass the posted $this-data through internally to the controller instance without having to rePOST via HTTP and redirect.

    setAction is much faster (and no passing of $this-data is needed!)
  • Nice Miles. It should be added to a list of "Ways to speed up CakePHP". This is extremely useful on high traffic sites.
  • Like Matt Curry, I also called an action in the same way. Read from the API that you can also pass parameters.
    Y.O. Morales ⋅
  • I feel you can come to situation when redirect better suited then setAction. For example post request should redirected (remember PRG pattern).
  • This is awesome, Miles. I had never heard of this method before either. I only wish you had a Retweet button on your blog so I could more easily pass on the word!
    Brendon Kozlowski ⋅
  • Another Cake function I had no idea existed. I had just been calling the action directly and then rendering like this:
    $this->listing();
    return $this->render('listing');
    Matt Curry ⋅
  • Nice... I wasn't even aware of this function. I'm sure this will come in handy.
  • Great! It's a really usefull tool.
    In this example I would prefer throw a 404 error, but the setAction has lots of uses
    Thanks.