Category Archives: CakePHP 2.0

Use your own style for a form

When you create a new form with CakePHP, it sets automatically some style around your inputs. We will see here how to get rid of it and use our own style.

echo $this->Form->create('Post', array('inputDefaults' => 
array('div' => false, 'label' => false, 'error' => false)));

We create a form, the first parameter is the name of your model, the second is an array of parameters but I will talk only of the key “inputDefaults” which deals with all of your inputs in this file.

  • div => (true or false), sets or not the div wrapper
  • label => (true or false), sets or not the label
  • error => (true or false), display errors or not

And then for one input, the title input for example.

echo $this->Form->input('title', array('class' => 'span3'));
echo $this->Form->error('title', null, 
array('class' => 'help-block alert alert-error', 'wrap' => 'p'));

The first line we set the input “title” with the class “span3”.

The second line is there to print the error if one occurs after the validation process, and of course, we set some classes and a wrapper element, here a paragraph, but you can put a span, a div. And voilà !

How to set HABTM

I saw few documentation about the HABTM relation, so I thought of writing some few things I have found out after my researches. Here I will show a HABTM with two tables, Posts and Tags. A post can have many tags and of course a tag can belong to several posts.

Database (posts table taken from CakePHP blog tutorial)

CREATE TABLE posts 
(
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

CREATE TABLE tags 
(
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(50) NOT NULL
);

CREATE TABLE posts_tags
(
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    post_id INT NOT NULL,
    tag_id INT NOT NULL
);

This is just example tables, you may add your engine, encoding and put the fields you want.
Just remember that the associated table name must by convention have the name of the two tables you link, and in alphabetical order. It is posts_tags and not tags_posts ! ! ! But you are not forced to use conventions, they are just convenient, and there I will use them.

After the database, here comes the time of setting the models. I will only show the important part about HABTM in them.

Post Model

public $hasAndBelongsToMany = array
(
	'Tag' => array
	(
		'className' => 'Tag',
		'joinTable' => 'posts_tags',
		'foreign_key' => 'post_id',
		'associationForeignKey' => 'tag_id',
		'unique' => false
	)
);

Tag Model

public $hasAndBelongsToMany = array
(
	'Post' => array
	(
		'className' => 'Post',
		'joinTable' => 'posts_tags',
		'foreign_key' => 'tag_id',
		'associationForeignKey' => 'post_id',
		'unique' => false
	)
);

You can also create a model called PostsTag, if you need. Again with the convention, alphabetical and plural for the first model, not the second, it is PostsTag and not TagsPost.

Now let’s go to the controller Post. For what I saw, you can’t save your new post and its tags in one shot. It comes from the fact that you need the id’s post to save the association.

So first we save the post, nothing new here. You may just send the Post content, test if it was successful, I just go to the essential.

	$this->Post->save($this->request->data);

Now we have the id of the post in $this->Post->id. We just have to save our tags and add the id in its structure. Here we assume $tags is an array of tag you get from your view.

	foreach($tags as &$tag)
	      $tag['Post']['id'] = $this->Post->id;

        $this->Post->Tag->saveAll($tags);

There we had the id to all our tags and save them all in one shot. What’s magic here is that cakePHP directly save the tags but also the associations between the tags and the post.

Now let’s say you want to erase all the associated tags of one specific post, you can do like this.

	$this->Post->PostsTag->deleteAll(array
        ('PostsTag.post_id' => $this->Post->id), 
        false);

And if you just want to erase one association, just specify the id of the post and the id of the tag you don’t want anymore. And voilà !

HABTM are tricky, I may not be very clear to explain this point, and I surely don’t know the whole about it, don’t hesitate on correcting me if needed 😉

Save a field to default value of your table

A small issue you can have when you use CakePHP is when you want to save your current model using $this->request->data and one of your field can be left to the default value of your table. Enough blabla, let’s see an example to understand the problem.

Table posts (taken from http://book.cakephp.org/2.0/en/getting-started.html#creating-the-blog-database)

CREATE TABLE posts (
    id INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    title VARCHAR(50),
    subtitle VARCHAR(50) DEFAULT 'no subtitle',
    body TEXT,
    created DATETIME DEFAULT NULL,
    modified DATETIME DEFAULT NULL
);

I only added the field subtitle for this example which is set to ‘no subtitle’ by default, I know this example is useless, you could put NULL instead or whatever you want.

Here’s a part of the Post model validation.

public $validate = array
(
	'subtitle' => array
	(
		'rule' => 'notEmpty',
		'required' => false,
		'allowEmpty' => true
	)
);

Basic validation, we put the notEmpty rule, then not required and allowEmpty to true. It can be tricky to see notEmpty and allowEmpty, but the first is the rule saying we accept whatever we type in. The other says we can left it empty. And required means that the array key subtitle does not have to be present in $this->request->data to validate, which is a part of what we need.

More about it http://book.cakephp.org/2.0/en/models/data-validation.html

So if you create a new post using a form and leave the subtitle field blank, CakePHP will set $this->request->data[‘Post’][‘subtitle’] equals to “”. It becomes an issue, because, it will save in your database the subtitle but with an empty character. The best is to let the database do the job of setting by default its value.

I had a little “idea” you can use in your controller :

if(empty($this->request->data['Post']['subtitle']))
{
    unset($this->request->data['Post']['subtitle']);
}
/* ... then you save your data */

This would unset from $this->request->data the subtitle key, so Cakephp will not try to save this field, and the database will deal with the default value and in our example set it to ‘no subtitle’.

You can probably write it in the beforeSave function to make it more proper and make it work for the edit action too.

I believe it’s not the best way to make it, leave a comment if you have some better ideas 😉