Serializeable behavior for CakePHP

For a project i’m working on i needed to serialize a number of arrays before they could be saved and since i don’t like to serialize and un-serialize all those arrays in each controller action, i wrote a behavior to take care of this.

It serializes all array data before it is saved into the database and when you do a find it will unserialize all data(including related model data).

Download serializeable behavior

Filed Under: CakePHP, Code, English - read on

Easy forms with CakePHP

Last night i was thinking about the fact that in some cases i can’t use bake to re-bake my views to contain a new field for example. CakePHP also wants you to not write the same code twice, but what about the views. Every time you add a field to your model, you either need to re-bake your views, or update both the edit and add template files. You could use $form->inputs(); but the helper creates form fields for database fields you probably want to keep hidden (_count fields for example).

I can think of two solutions for this problem, namely.

1. Go the partial way (like rails uses it). Basically you make an element with all the form fields you want to display and use the $this->renderElement in the view to display the form fields.

2. Define public fields in your controller and use those. An example below:

post has the following fields:
id, title, content, created, comments_count.

we only want the user to be able to edit the title, content and maybe the created fields.

in our controller we create a new var: “publicFields”


var $publicFields = array('title', 'content', 'created');

you can set this var in the beforeRender callback like this: (controller)


function beforeRender(){
$this->set('publicFields', $this->publicFields);

and in your view you render the inputs using the inputs function of the form controller like:


$form->inputs($publicFields);

this way you only have to add a new field in your database and in the $publicFields array and all your forms update automagically.

What do you use to reduce the amount of code you need to edit for a change in the database?

Filed Under: CakePHP, Code, English - read on

Cakephp routing: prefixes and forms

Prefixes are a nice way to define routes for groups of actions, (see the cookbook).

All admin_ actions go with the /admin// url
For my project i needed both an admin route and a dashboard route, so i made prefixes for them like:

(/app/config/routes.php)

Router::connect('/dashboard/:controller/:action/*', array('prefix' => 'dashboard'));

Now in the view you can use the following:

echo $html->link('Profile', array('controller'=>'users', 'action'=>'dashboard_profile'), array('title'=>'Edit profile'))

But when it comes to forms things start to break.

For example the following:

echo $form->create('User', array('action'=>'dashboard_profile'));

or this one:

echo $form->create('User', array('url'=>array('controller'=>'users', 'action'=>'dashboard_edit')));

generate the following (wrong) link: /users/dashboard_profile/1

While it does point to the right action (dashboard_profile) you get an error message explaining that this is a private action. The only way to use this private action is to use the prefix method.

The reason for this is that the form generate part in the cake code doesn’t do anything with the routing, thus ignoring the prefix we’ve set.

After some digging in the api i found a cake-way to generate the right url:

echo $form->create('User', array('url'=>$html->url(array('action'=>'dashboard_profile'))));

We use the html helper, that does parse the prefix part and generates a nice correct url, namely: /dashboard/user/profile

The html helper does check for any prefixes in the routing and generates the right url.
Combining the form->create and the html->url we get the right url.

Filed Under: CakePHP, Code, English - read on

This week in Cake #2

Tim Koschützki of Debuggable Ltd. explains how to bend the find() method to your own needs. In the comments there are also two links to other posts about this subject, namely to: Daniel Hofstetter (CakeBaker) - Defining custom find types and Nate Abele (on C7Y) with Best Practices in MVC Design with CakePHP

Peter Butler from Studio Canaria created a CakePHP Currency Conversion Component that updates right from the internet. This way it’s always up-to-date with the latest currency conversion rates.

Teknoid as a nice post about form security on his blog: nuts and bolts of cakephp

And last but definitely not least: Cake 1.2 RC2 has been released!! See the post in the Bakery

That’s it for this week. Next week i’ll be in France relaxing at the coast, so no update then ;)

Happy Baking!

Filed Under: CakePHP, Code, English - read on

SaveAll for CakePHP (part 3)

Recently i needed to save multiple entries for the same model and i thought saveAll should do the trick.
After trying some different approaches i found the right one. This approach can be used for both new entries and to update entries.

Controller

(/app/controller/tasks_controller.php)

New entry:

function add() {
	if (!empty($this->data)) {
		$this->Task->create();
		if ($this->Task->saveAll($this->data)) {
			$this->Session->setFlash('Tasks saved');
			$this->redirect(array('action'=>'index'), null, true);
		} else {
			$this->Session->setFlash('Tasks could not be saved');
			$this->redirect(array('action'=>'index'), null, true);
		}
	}		
}

Edit entry:

function edit($todo_id=null){
	if (!empty($this->data)) {
		if ($this->Task->saveAll($this->data['Task'])) {
			$this->Session->setFlash('Tasks saved');
			$this->redirect(array('action'=>'index'), null, true);
		} else {
			$this->Session->setFlash('Tasks could not be saved');
			$this->redirect(array('action'=>'index'), null, true);
		}
	} else {
		// Find ten tasks to edit
		$tasks = $this->Task->findAll(null, null, null, 10);
		$this->set('tasks', $tasks);
	}
}

View

For a new entry:

<?php echo $form->create('Task');?>
	<fieldset>
 		<legend><?php __('New Tasks');?></legend>
	<?php
		// Lets generate 10 task input fields
		for(i=0;i<10;i++){
			echo $form->input($i.'.name');
		}
	?>
	</fieldset>
<?php echo $form->end('Submit');?>

To edit entries:

<?php echo $form->create('Task', array('url'=>array('action'=>'edit')));?>
	<fieldset>
 		<legend><?php __('Edit Tasks');?></legend>
	<?php
	
		// Loop trough the ten tasks and create form fields. We need at least the ID to update a task.
		$count = 0;
		foreach($tasks as $task){	
		echo $form->input($count.'.id', array('value'=>$task['Task']['id']));
		echo $form->input($count.'.name', array('value'=>$task['Task']['name']));
		$count++;
	}
	
	?>
	</fieldset>
<?php echo $form->end('Submit');?>

How it works

It’s not much different from the previous saveAll parts, the only really big change is that the form field names are a bit different and you need to specify the right model/array to save in the controller ( the $this->Task->saveAll($this->data['Task']); part).

Filed Under: CakePHP, Code, English - read on