CakePHP: Watch Out for the compact() Function

Okay, so maybe this caveat isn’t specific to CakePHP since the function is a native PHP function, but I just spent the past hour trying to figure out why variables that I clearly set in my controller weren’t available to my view. I’ll never get that hour back, but maybe I can keep someone else from losing it. As far as I’ve been able to tell, this particular behavior isn’t documented, but I haven’t done any kind of exhaustive search.

The compact() function exists as a shorthand function to create an array of variables. The PHP documentation does a nice job of detailing the function and providing a few simple examples, so I won’t belabor that point. Within CakePHP, it’s a handy shortcut for passing variables from a controller to a view. In my case, I have a vendor application form with a few variances that I need to manage. Without using compact(), a snippet from the action method of my VendorsController would look like this:

public function index() {
   …
   $vendor_type      = ‘Food’;
   $application_type = ‘Commercial’;
   $states           = $this->Vendor->Address->State->find ( 
      ‘list’, 
      array ( ‘order’ => ‘State.title’ ) 
   );

$this->set ( ‘vendor_type’, $vendor_type ); $this->set ( ‘application_type’, $application_type ); $this->set ( ‘states’, $states ); … }

That’s not an intolerably verbose example, but it’s easy to see how it could become so. Being the laconic kind of guy that I am, though, I decided to use the compact() function to cut down on the verbiage. Now my action code looks like this:

public function index() {
   …
   $vendor_type      = ‘Food’;
   $application_type = ‘Commercial’;
   $states           = $this->Vendor->Address->State->find ( 
      ‘list’, 
      array ( ‘order’ => ‘State.title’ ) 
   );

$this->set ( compact ( ‘vendor_type’, ‘application_type’, ‘states’ ) ); … }

That’s a little better, but there’s a caveat.

Using this example, I went to my view code and tried to access my $vendor_type variable only to get an undefined variable error. After working my way through various debugging techniques, I pulled out the big gun. In my view, I dumped the output of PHP’s get_defined_vars() function and found that my $vendor_type variable had been renamed to $vendorType. I don’t know if the change was made by CakePHP or by PHP itself, but I didn’t expect it (nor do I have any reason to believe that I should have expected it) and the difference is as fatal to an application as it is obvious.

Subscribe17 Comments on CakePHP: Watch Out for the compact()...

  1. ADmad said...

    Its done by CakePHP :) Cake’s convention is that view variable names should be in camelBacked form.

  2. Adam Royle said...

    It’s changed by cakephp – nothing to do with the compact() function. Personally I don’t agree with cakephp renaming it without a warning, but I guess they want to standardise variable names.

  3. Brian d. said...

    I’ve been bitten by this same problem before and now I just use $this->set(‘var’, $var) most of the time instead of using compact().

  4. Mike said...

    Good catch.. I’m sure I’ll get bitten by that sometime. It definitely follows the same sort of conventions cakephp normally enforces so I would have to agree with Adam that it’s not a compact() thing.

  5. Ferdinant said...

    It’s indeed Cakes Naming Convention that takes care of this. They might take a note on this one at the cakephp book, if it comes to me. :)

  6. Rob Wilkerson said...

    Thanks for the info, all. I agree that it’s not inconsistent with CakePHP’s conventions, but I do wish its use was documented (or documented more prominently). Renaming variables that were explicitly set is bound to cause confusion among those that, like me, are new to CakePHP.

    I’d probably go one step further and argue that it’s not a good practice, but if it’s well documented then at least it won’t be a surprise.

  7. dr. Hannibal Lecter said...

    That’s strange.. If this is a cake thing, how come it doesn’t happen when you’re not using compact? This is completely absurd. Same method, different output results? Why? If I set my variable as $mega_crap I want $mega_crap and not some other var name.

    Weird…

  8. ADmad said...

    Anyone can submit updates to the cookbook. And that anyone can be you :)

  9. Rob Wilkerson said...

    Fair enough, ADmad. And quite true. I’ll take a look and try to figure out the best way to do so this week. Thanks for the, um, encouragement. :-)

  10. JIESCH said...

    $this->set (‘data’ , $res=compact ( ‘vendor_type’, ‘application_type’, ‘states’ ) );

  11. jeff keen said...

    compact() takes another variable that solves the underscore problem.

    $this->set( compact(‘vendor_type’), true);

    Include “true” when using variables with underscores, and then they will show up in your view as is.

  12. Rob Wilkerson said...

    Hey, Jeff – would you mind citing your source for that info? It sounds great, but I can’t find any indication of that second parameter in either PHP’s compact() function or in CakePHP’s Controller::set() method.

  13. Jeff Keen said...

    I apologize. I did that from memory and it was wrong. The second param is for the “set” method… and you want to use “false” rather than true. No detail of this in the CakePHP API, but it goes like this:

    $this->set(compact(‘foo’, ‘bar’, ‘vendor_type’), false);

    Taylor Luk pointed it out in a comment on the following blog: http://myeasyscripts.com/loudbaking/beware-of-the-underscore/#comments

    This was brought up in Google Groups: http://groups.google.com/group/cake-php/browse_thread/thread/a136b91f49f1befb?pli=1. It appears that it will be resolved in version 1.3. Hope this helps.

  14. Jesse said...

    Hump notation for the win! After switching to hump/camel notation for a couple years I will never go back to the underscore format! I can’t say I have ever ran into this simply because I always use hump notation for my variables in Cake.

  15. Nate Abele said...

    Hey Rob,

    Yeah, I always thought that was kind of a dumb “feature”, so it’s been reverted in 1.3. Your variables will now always show up exactly as you set them, regardless of how you pass your parameters to set().

  16. Rob Wilkerson said...

    That’s awesome, Nate. Thanks for the heads up.

    Like most things it’s not such a big deal as long as you know to expect it, but I do prefer that my tools not modify entities that they are explicitly asking me to set. This is a decision that I’m glad to see reversed (even if it may cause me some short-term upgrade pain since I may have tweaked my view instead of my controller).

  17. Rob Wilkerson said...

    jesse: I’m conflicted and admittedly inconsistent on this issue. Usually, when writing in case sensitive languages, I’ll use lower_with_underscores and headlessCamelCase with case insensitive languages, but especially with variable names (as opposed to class/method names), I’m not as consistent as I really should be. I kind of like both and tend to fluctuate far too much. :-)