Programmatically add form control for associated data

Assume that I have Authors model which has many Books.

In my create Authors form, I know how to create form control to add several Books together with Authors.

The problem is when design Create form for Author, I have to specify determinated array index number for each book. Another word, I can only add certain number of books by its array.
I want to have (+) and (-) buttons which will programmatically add more or delete some of book input control using JavaScript.
The adding and removing HTML form tag can be done, but I dont know how to name those controls to be valid for patchEntity() in controller.

Tried <input name="books.[].name"> but it didn’t work.

I know <input name="books.0.name"> for 1st book, and <input name="books.1.name"> for second book, and so on, but then how to implement that with (+) and (-) button?

What I did in the past is that using JQuery to get number of current book input controls, then create new control using pre-defined template, then replace the index in name and id attribute using JavaScript.

Is there any better way or CakePHP native way of doing that? Thank you very much.

I do this things manually with HTML DOM Document createElement() Method
and about names - just look in browser debug after doing 2 books inputs that works.

Would it be an option to have a hidden text field that you programmatically populate with the extra books they are adding [and removing]?

That way you’re not trying to pass back an indeterminate number of fields the view won’t know about. If you don’t give your created fields names they shouldn’t get posted back. This also means you can use form protection which can be handy.

Thank you jarekgol, but I know how to create HTML element, just dont know how to name them so that the post data will be valid for patchEntity in controller.

Hi Jawfin,

Thank you for you response, but I quite don’t get what you mean.
Would you please explain a little bit more?

Why should I have another hidden text field? what does that hidden text field do?

Thank you very much

You can add a virtual string field to the table the form posts back into, lets call it allbooks, so something like: -

<?= $this->Form->create() ?>
...
<?= $this->Form->hidden('allbooks') ?>
<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>

Now before you do the submit have some javascript which iterates through all your created fields (don’t create them with name but just id as you don’t want them being posted) and put their info into your allbooks field, in a JSON format.

Then your controller can break it down and build whatever query needed to update your detail table.

That is how I do it - as you can log the json directly in an audit table if needed, you only need to look at one field for debugging, and is easy to build bulk fake data for testing.

Thank you Jawfin,

It’s an interesting approach. I will consider this.

Short:
name="books[int++][name]"
where int++ is next number from your JS.

Here is an example adapted for you from my working program in v3.1

//controller
...
$book = $this->Books->newEntity();
$this->set('book',$book);
...
//view
echo $this->Form->create($book);
echo $this->Form->input("books.0.name");

There are many ways to do this, from purely JS to purely PHP to Ajax methods that blend the two. It seems that you have a handle on some of those options.

To answer the general question of “Is there any CakePHP native way of doing that”, the answer is no, AFAIK. If you use a PHP or Ajax method, it would involve a Cake controller and view, as everything does, but the implementation of those is just going to use standard Cake methods that you’re probably already familiar with. You might use the Cake form helper, for example, to build the resulting view, but there’s no magic method in it that will name your fields for you in this scenario.

1 Like