CakePHP Beginner Tips

Based on CakePHP1.x. Valid for 2.x (see updates below).

Apache Server

For Windows use WAMPServer2 and select "Apache" => "Apache Modules" => "rewrite_module".
Thats all there is. Cake should now work out of the box without any other modifications.

Database

Use UTF8 and utf8_unicode_ci (not "utf8_general_ci") for all tables. "general" might be a little faster, but is also more inaccurate, e.g. in sorting. I found the speed argument to be negligible and not measurable in most cases.

I don’t use NULL as default value for normal text fields, if I can avoid it. There are some issues one needs to work around, otherwise. So always try to use default ‘0’ for int fields, default ” (empty string) for varchar/char etc. For a list of database related issues see problems-with-null.
For foreign keys you should use NULL, though (as 0 or empty string do not make much sense here).

Use tinyint(1) ONLY for boolean (=toggle) fields (0/1) – represented by checkboxes. For anything else (select box with 0,1,… and more elements) you need tinyint(2) or even int(x).
Always use "id" as primary key". This convention helps in the long run. It should be int(10) UNSIGNED – a so called AIIDs. Another option would be to use UUIDs. They are unique across the whole database and are char(36).

The date fields "created" and "modified" are handled by cake automatically if present on every INSERT/UPDATE. So don’t call them anything else if you don’t have to.
Just make them of type date or datetime and default them to NULL.

The label fields "name" or "title" will automatically be used by CakePHP as "displayField" if found as table fields. If you do not specify one of those two, you should manually declare the displayField used for find(list) etc;

public $displayField = 'label';

Other automagic fields are of type text/mediumtext/longtext which result in a textarea.

I recommend to stick to conventions and underscore + lowercase all table fields: "last_login" (instead of "lastLogin" or even worse "last login") etc.

Do not use "enums" as they are not supported. There are actually even better ways to workaround this.

The "length" of a CHAR/VARCHAR field gets read out by Cake with "DESCRIBE" and is stored in the cache.
It is used to set the "maxlength" attributes for <input type="text"> form fields, which are created by the form helper. If you want the username only to be 20 chars, the form will not allow 21. Check it out. You still need to validate this, though, in the model (as it can be hacked).

UPDATE: Some NULL issues can actually be resolved. NULL / NOT NULL makes a difference for validation as they will add "required" to the form fields. So make sure you also check this out and maybe do use NULL where applicable.

General tips

Always develop with debug > 0 (usually 2 for detailed sql queries at the bottom).
Always deploy with debug = 0.

Find conditions

This doesnt work:

->find('all', array(
    'conditions' => array('user_id' => '', 'user_id' => NULL)));

same with

->find('all', array(
    'conditions' => array('OR' => array('user_id' => 2, 'user_id' => 3))));

You cannot use array keys twice in the same array. Either put them in a subarray inside the array or combine them right away:

->find('all', array(
    'conditions' => array(array('user_id' => ''), array('user_id' => NULL))));
->find('all', array(
    'conditions' => array('OR' => array('user_id' => array(2, 3)))));

For the second example CakePHP with automatically use IN (…) instead of =.

Forms

If you need to pass default values to select fields or any other input, do NOT use inline params like ‘selected="xyz"’. As soon as you submit your form it will restore exactly the same default value – no matter what the user changed. But that’s not common sense. Usually, if an error occurred and you have to correct your input you want everything to be same as before – only changing the required field.
So the correct approach is to pass it from the controller:

if (!empty($this->data)) {
    // validate and save
} else {
    // now thats the important part here
    $this->data['Model']['field'] = 1; // or whatever your default value is supposed to be
}

For details see here.

If you want to submit the form to the same url, use:

$this->Form->create('OtherModel', array('url' => '/' . $this->params['url']['url']));

It will preserv all params like "/controller/action/123/uid:xyz" (usually you would submit to "controller/action" or "controller/action/123").

Links

Always use arrays for internal links:

$this->Html->link(array('controller' => 'x', 'action' => 'y', 'some_pass_var', 'some_named_var' => 'z'));

This way your application is flexible and you can always route the url. If you hard-code it as string this won’t be possible.

AppController callbacks

beforeFilter and beforeRender as well as afterFilter are available for app wide stuff to be handled.
Be careful though:
If you want to use whatever you do there in the layout (on all views), you need to use beforeRender() only. That’s because only beforeRender() is triggered if an error occurs. The other 2 not!
Especially if you need the variables passed to the view in some elements etc you will otherwise get notices and errors.

Update 2012 Cake2.x

For Cake2.x the form will always submit to itself by default. No need to overwrite the url here:

// This suffices
$this->Form->create('OtherModel');

You do not check on if (!empty($this->data)) {} anymore, but use if ($this->request->is()) with post/put instead.
Also use $this->request->data instead of $this->data in the controllers.

Note that in 2.x you should move away from named params ASAP and use query string instead.

The rest should still be valid.

Update 2014 Cake2.5

It is now possible to have primary keys, foreign keys and tinyint(1/2) as well as some of your other int fields as "unsigned". This can be useful as it saves a little bit of space in the long run, but also ensures there are no negative values to care about. It is good practice to do so. The schema fully understands that now.

4 Comments

  1. Pretty good tips. One tis one:

    $this-&gt;Form-&gt;create(‘OtherModel’, array(‘url’ =&gt; ‘/’.$this-&gt;params[‘url’][‘url’]));

    Why not just use $this-&gt;here instead of $this-&gt;params[‘url’][‘url’]

    Thats what I have been doing forever

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.