Saturday, May 9, 2009

Essential CakePHP tips for starters

It's been 3 months since I started using CakePHP and I have to say I'm having a great time using it. Before trying it out I was using my own little framework and tried a bit of Zend Framework. I think the best thing about CakePHP is that it's a full stack framework, which means you can take advantage of a lot of built in features. It takes some time to get used to especially if you come from a do-it yourself background (like me :), but in the long run using its conventions and built in features will save you a lot of time.

First off, I will assume that you already know the basics (at least Chapter 1 & 2 of the CakePHP manual). Also, this post was inspired by Teknoid's 15 Essential CakePHP Tips which is a great read. I suggest you read it first before reading this (my favorites are #5 I’m losing the extra URL parameters when paginating and #9 Avoid using the $uses array)

Disclaimer: I'm no CakePHP expert yet so feel free to suggest or correct me if I'm wrong!

Let's start!

HABTM query
Let's suppose recipes has and belongs to many tags, how do you query recipies that contain a certain tag? There are many ways to do this, as documented on the CakePHP manual. I think the simplest and cleanest way is given on the first example:
$this->Recipe->bindModel(array('hasOne' => array('RecipesTag')));
$this->Recipe->find('all', array('conditions'=>array('RecipesTag.tag_id'=>124)));

Use named parameters
CakePHP works better with named parameters (/name:value), especially when you are using pagination and other helpers. But if you don't want to, classic style parameters (?name=value) can still be accessed via $this->params on the controller

Paginate an unrelated model
If you need to paginate a model that's not in the current controller, use loadModel('model').
$this->paginate = array('limit'=>20);

Getting data from $this->params and $this->data
The usual way is to get request parameters directly is to get them from the $this->params (or $this->passedArgs) and $this->data array directly (eg. $input = $this->params['named']['input']). But it's a little dirty because PHP shows a notice if the key does not exist in the array. To prevent this, I created two functions on my app/app_controller.php file that gets data from the param and data array, in case the key does not exist, it will just return null.

function getParam($key)
// eg.
// $somevalue = $this->getParam('named.somekey');
return Set::classicExtract($this->params, $key);

function getData($key)
// eg $name = $this->getData('')
return Set::classicExtract($this->data, $key);

(for more array functions, check out CakePHP's Set component)

Secure forms using the security component
If you want to quickly secure your forms, CakePHP can do this automatically by using the Security component.

Keep links relative. Use $html->link(), or Router::url()
If you want your app to be portable, take advantage of CakePHP's built in URL functions to keep your links working wherever you install your app (eg root directory or sub directory). Use $html->link() for generating full link tags, or Router::url() if you only want the URL (eg. using it inside javascript or inside the controller).
Hint: if you want to get the full URL (including http://) use Router::url('/controller/action', true)

Use form helper
Because writing a lot of forms by hand is boring :) You will also take advantage of other built in functions, like displaying validation error messages.

Use the same view for add/edit forms
Most of the time add and edit views are just the same. To avoid code duplication, you could set the add action to also use the edit view. Just add $this->render('edit') at the end of the action.
function add()

Looking for more tips?
Check out Matt Curry's Super Awesome Advanced CakePHP Tips


  1. All good tips apart from "Make validation errors appear".

    Validation errors display fine for me without needing to pass invalidFields() to the view.

  2. @richard thanks for the correction, maybe I was doing something wrong. I'll remove it from the post.

  3. Although there's nothing wrong w/ the way you're getting the model in the paginate tip, the preferred way is:
    This will add the model to the current controller.

  4. @matt thanks! I updated the post