Multirecord helper/behavior

By Eskil Mjelva Saatvedt (eskil)
Edit and add multiple record forms. A behavior and helper working together to create multiple records form in the same way Form->inputs() work. With a very few lines of code, go from one to many records at once.

This project stated when I was to look at a component and redid it to learn more about components. I chose a multiple record component by Marcel Raaijmakers (Marcelius) and his article http://bakery.cakephp.org/articles/view/how-to-create-multirecord-forms as my project.

The main goal was to be able to edit, add and delete multiple record set. Add 4 posts at once, edit 3 users at a time, delete all the selected files.

The problem was to get the form helper to create a form, which matched the data set Model->saveAll wants.

As Marcel Raaijmakers points out: [parafrase]"The form need to be on a format saveAll() can handle, so the Helper has to create a data set on the right format when posted. It has to be on the form ModelAlias.{n}.fieldName while when it comes from Model->find('all',) it is on the form {n}.ModelAlias.fieldName."[/parafrase]

At first I tried to write a component to rewrite the parameter array sent to the functions on the controller. This way the data format would fit what Model->saveAll() wants. This proved harder than I was prepared for, and instead of a component and a helper, it became a behavior and a helper.

The behavior do the save, find and delete operations. And the helper duplicates the Form->inputs() functionality.

Other important aspects for the project:
* Usability
* Stick with convention
* Only minor changes to controller and views to make it work

Content:

* Description-this page.
* Helper code-page 2
* Behavior code-page 3
* Full working example-page 4

Behvior

FindMulti

To find data I did this
Download code $this->data = $this->Model->findMulti($ids);

Instead of
Download code $this->data = $this->Model->find('all');

The $ids are either a string on the form $ids = "3 5 8" or an array $ids = array(2,5,8)

When Cake parses the url and sent parameters to the function, an url on the form
http://host/app/controller/edit/3+5+8 is sent to the controller function as a string on the form "3 5 8".

This is the reason I want the funtion to take in the string. To find the multi record set, you only need to add the ids to the url.

The function findMulti() returns an array on the form ModelAlias.{n}.fieldName, a normal find('all') which returns data on the format {n}.ModelAlias.fieldName.

SaveMulti

For edit and add I also wanted to save multiple records
Download code $this->Model->saveMulti($this->data);

Instead of
Download code $this->Model->save($this->data);

By manipulating the post data, the data is now on a form where it can use saveAll($this->data) the only thing done, is validating all, before the save goes trough.

DeleteMulti

To delete all I did this
Download code $this->Model->deleteMulti($ids)

Instead of
Download code $this->Model->del($id)

Here again the $ids is a string on the form $ids = "3 5 8" or an array $ids = array(3,5,8) in the same way the edit() function did.

MultipleRecords Helper

The helper is made to supply the Form helper they are used together. It only needs two functions.

$multipleRecords->inputs()

To add the multiple records form
Download code echo $form->create('Post', array('url'=>array('action' => 'edit')));
echo $multipleRecords->inputs();
echo $form->end('Submit');

Instead of
Download code echo $form->create('Post', array('url'=>array('action' => 'edit')));
echo $$form->inputs();
echo $form->end('Submit');

This will create a form with the same number of records as is in $this->data. It expects the data to be on the format the MultipleRecords Behavior provides.

The inputs() function require Form->create() to be run first.

$multipleRecords->add()

The other function is only a helper to add one more empty record set than already displayed on screen.
Download code echo $multipleRecords->add('Add Post');

When this form button is pressed, it posts the data to the add form, sending it back to the view, while not saving it. This is done so the entire form set is generated again, with one more empty set. The reason for this is to make it work with the security component. As the security component hashes the fields, an ajax call to add an empty set would fail when the form is finely posted. It also keeps whatever you have already written, but not saved. The same would go for script calls to remove single records.

All in all

It did work. I have kept the manipulation of data in the behavior and the manipulation of the form helper inside the MultiRecord helper. Using Form->inputs() in stead of a list of input is one of my favourite cake functions. This makes form generation easy.

Page 2: Helper code

Comments 855

CakePHP Team Comments Author Comments
 

Comment

1 Nice implementation

Great to hear someone picked up my article :-D

I think you implementation is more cake-ish then mine because of the extended form helper capabilities you added. It's also good to know that more people ran into the same problems and I hope these articles will trigger people to further develop this kind of behavior.

Like you, I also started to do a rewrite from {n}.ModelAlias.fieldName to ModelAlias.{n}.fieldName and visa versa, but as you said its not easy, mostly because it gets complicated with deep (habtm) relations. But I'm still working on that so eventually this would also work: (I hope :-p)

$this->Post->save($this->data); //so no extra component is needed

Anyway, great extensive article!

Marcelius
Posted Dec 31, 2008 by Marcel Raaijmakers
 

Comment

2 Small update


I changed the example code so it now works correctly.

Thank you Marcelius.
Posted Jan 5, 2009 by Eskil Mjelva Saatvedt
 

Question

3 I don't know if it's bug, but it's not working on version 1.2.2.8120

Hi Eskil,

Nice post, but it's not working on version 1.2.2.8120.

When I try add new post filling the form e pressing Add Post button nothing happens and I notice message appear on screem.

Notice (8): Undefined variable: numberOfRecords [APP\views\posts\add.ctp, line 8]
And still the label "Add Post 1" that should be increment, don't do!!!

I'm new on cake and disable cache on core configuration.

Thanks.
Alexandre
Posted Apr 27, 2009 by Alexandre Correia
 

Comment

4 Thanks for the reply

Hi Eskil,
And still the label "Add Post 1" that should be increment, don't do!!!

I'm new on cake and disable cache on core configuration.
Thanks.
Alexandre

I checked the code, and it works for me, at cake version 1.2.2.8166

Be careful to don't mix the add and the edit functions in the controller but add both.
Posted Jul 30, 2009 by Eskil Mjelva Saatvedt