The Dark Side of CakePHP’s Automagic
-
Friday, September 4th, 2009
Automagic is authoritatively defined by wiktionary as “Automatic, but with an apparent element of stage magic. Commonly used in computer and other technology fields, referring to complex technical processes hidden from the view of users or operators.”
While, Cake’s automagic features are what make the framework useful, they can often turn around and bite you. Though it is not necessary to understand the full implementation details of every automagic feature, it is important to be aware of some common issues.
Retrieving Associated Data
You can link models together using associations. When using the find function, Cake automagically fetches associated data. This may cause the database to execute queries for data that is never used. The Containable Behavior is one of the best solutions to reduce the amount of data fetched.
The Containable Behavior is covered pretty well in the CakePHP book. Basically, it allows you to specify which associations you want to be included in a find query. It’s fairly new to CakePHP; before it was introduced in CakePHP 1.2, the recursive property and/or the undbindModel method were primarily used to pare down find results (if they were even pared down at all). The Containable behavior allows you to accomplish the same task in a much simpler and cleaner way.
Saving Has and Belongs to Many Data
An example of an association type is hasAndBelongsToMany (many to many). Again, the CakePHP book is a good source for learning how to save hasAndBelongsToMany data.
A key line in the docs is the following: “Cake will delete all rows on the join table before saving new ones.” It is important to be cautious when saving data with hasAndBelongToMany associations. The following example illustrates a reason for such a caution:
$options = array(
‘conditions’ => ‘Recipe.id’ => $recipeId
);
$data = $this->Recipe->find(‘first’, $options);
//Changes to $data
$this->Recipe->save($data);
If you had any hasAndBelongsToMany associated data, such as RecipeTags, these records would all be deleted. This was an unfortunate lesson I learned on a recent project.
Debug Level and Caching Behavior
In /app/config/core.php, you can set the debug level. There are four levels, 0-3. In the comments of this configuration file, it explains what to expect at the various levels. For levels greater than 0, model caches are refreshed.
If you are new to CakePHP, you may not understand what is meant by “model caches refreshed”. In app/tmp/cache/models you’ll find a file for every model containing a cached version of the database schema.
If you make a change to your database schema when the debug level is 0, these model caches won’t be refreshed; if you get errors or unexpected behavior after making a database change, a likely fix is rm -f app/tmp/cache/models/*.
If you ever decide to clear the cache by removing the tmp directory, remember to rebuild the directory structure and recursively set the permissions to be writable by the web server.
Be careful not to set debug to greater than 0 on a production system. Besides displaying errors and warnings, leaving your system in development mode will make requests take longer. CakePHP runs a describe query and rebuilds the cache file for each model in use.
AuthComponent Password Hashing
Cake’s AuthComponent can add authentication to a site quickly and easily. However, if you don’t understand all the trickery that goes on, you can encounter some issues when your implementation varies from the example in the CakePHP book. I recently ran into some trouble when building a registration form. As is typical of registration forms, there is a password field. I wanted to include all my validation rules in the model.
When I submitted the registration form, I would consistently get a password matching error, even though the password and confirm password were the same. I discovered the AuthComponent hashes the password field when you post data via a form. Though there are numerous ways to get around this, here is my preferred approach (this snippet belongs in the User model):
var $validate = array(
'confirm_password' => array(
'minLength' => array(
'rule' => array('minLength', 6),
'message' => 'Passwords should be at least six characters long.',
),
'matching' => array(
'rule' => 'comparePasswords',
'message' => 'Passwords do not match.'
)
...
);
function comparePasswords()
{
if(
$this->data[$this->name]['password'] ==
Security::hash($this->data[$this->name]['confirm_password'], null, true)
)
{
return true;
}
return false;
}
Notice how validation is performed on the confirm_password field. This allows you to specify length and other requirements on an unhashed version of the password. I typically display errors in list form. If you wanted to display an error message next to the password field, you would need to adjust the code to also invalidate the password field.
Posts Tagged ‘Auth Component’
Tags: Auth Component, CakePHP, Containable Behavior, hasAndBelongsToMany Association
Posted in Blogroll, php | 4 Comments »