Nested add forms of multiple tables via form buttons

I would like to implement add entities in one View, for example add a new song (unknown in DB) in a new song request. I don’t know how to access and implement multiple model’s data in one template.
songs have id, title, artist, album etc.
song requests have request_id, song_id (foreign key), date of request, user_id etc.

One way would be to make multiple hasOne relations and foreignKeys to add Form->control() for unknown song.title, song.artist etc.( Creating Inputs for Associated Data).

The other way (the correct one I guess) would be to implement the add() form of Songs if the user clicks on an “add new song” button.

In pseudo-code for add.php of song requests:

 <?= $this->Form->create($order) ?>
            <fieldset>
                <legend><?= __('Add Order') ?></legend>
                <?php ... echo $this->Form->button('add new song',  

echoing the actual add() template code of Songs
$this->Form->button(‘confirm new song’, [‘controller’=>‘Songs’, ‘action’=>‘add’, new song data fields (are no possible direct arguments for the add function)]);

?>

<?= $this->Form->button(__('Submit')) ?>
<?= $this->Form->end() ?>

The problem is that I want to retrieve the actual add form of Songs, while staying at the same page and same location (song request add template). Is it even possible to have form elements for successive models within the fieldset and before the submit button of the first model form?

I could also implement the add() controller code of songs into add() of song requests controller and retrieve data fields of one form. I don’t know which solution is DRYer, especially since it would not automatically take validation of the fields for songs into account.

The last option seems most feasible for me, so I want to implement the following form code after clicking a button:

<?= $this->Form->create($product) ?>
            <?php
                echo $this->Form->control('song_name');
               ...
            ?>
        </fieldset>
        <?= $this->Form->button(__('Submit')) ?>
        <?= $this->Form->end() ?>
    </div>

I did not find a direct way to run code from a button (even though I thought onclick would work as an HTML attribute?), so I implemented like this in the add.php of song request:

echo $this->Form->postButton(‘add new song’, ‘nested_song_form.php’);

with the former code in nested_song_form. However, clicking the button leads to no execution of this code, but staying on the same page with the error that the song request could not be saved.

Here are some very general principals to keep in mind.

Any entity with its associated data as returned from a query can be converted to an array like this:

//example query
$result = $this->Tenants->find('all')
    ->where(['Tenants.id' => $id])
    ->contain(['Warehouses', 'TenantWorkers'])
    ->toArray();

//pick off one entity
$entity = $result[0];

$array = $entity->toArray();

This ‘native structure’ for your data can guide you when trying to save posted data.

If you follow Cake’s conventions in naming, querying, and creating your forms, the data that posts back to your actions will match this structure. If it does, it will almost always save directly with no further fiddling (including associated data).

I don’t know that this will work 100% of the time, but as a general principle, if you are having trouble getting form data to save, just do a query to get a result matching the kind of data you want to save and compare the result structure to your posted data structure. Then correct your form helper calls to match the structure of the query result. Typically this will be a matter of ‘naming’ the fields correctly in the Form->control() calls.

Perhaps this will guide you to a solution.

I think I am not looking for any (existing) entity in this problem. These are my issues as I can currently indicate:

accessing non associated table requirements /options

  1. echo’ing Form->controls of non-associated DB fields can’t be checked whether the value is valid in a “foreign” form.
    1.1) echo’ing Form->controls of non-associated DB fields can’t list options that belong to the non-associated DB. For example, in the add order form I would like to add a new product and list available product.categories (equivalent to 'options' => $categories in the add form of products.).

insight in the controller
2) So far debug($this->request->getData()); in the (e.g. song requests / order) controller doesn’t give output. The song request / order is stored in the DB, but not shown in the index since song / product ID is missing (which I first need to assign by a new song / product Entity in the song request / order controller).

non-form button to add non-associated data fields on pressing
3) I am looking for a NON-Form button that simply executes the echo’ing code for non-associated fields within the form, without executing a form submit/post yet.

You can use Cake’s Modeless Forms to create any non-model schema and validation process you want.

If you make the values you want available on a variable and set the ‘options’ key in the control properly, you can accomplish this.

There won’t be any value here until you arrive with POST data from some form.

This sounds like something you could do with javascript and an ajax call.

Okay, so I have to create a form from scratch instead of implementing the baked forms.

In the example of Modeless Forms a new class is made for the custom form. I want to implement the new wholistic form in an existing controller, namely OrderController. In here, use Cake\Form\OneBigForm; still gives Class ‘App\Form\OneForm’ not found. Is this since I did not create a OneBigController, and therefore necessary to work with a custom form?

And are the explicit schemas of baked forms located somewhere? Such that I can copy these fields to the schema of OneBigForm and copy the validators from the Tables.

And to be sure, I really need Javascript and Ajax? What about the onClick HTML attribute? I was hoping for a local solution to add more Form->control fields with e.g. onclick - Run PHP code with click on Button - Stack Overflow .

PS

I was aware of this, but it didn’t show even after succesfully redirecting after the add form submission.

Yes. The baked forms are just a standard scaffold to get you rolling.

Is the fact that you use two different names here a typo in this message or your code? Did you include a use statement before your class declaration? And was your OneBigForm class properly namespaced?

Yes. If you want to change them I think these are the details

That might work. I have no specific knowledge here.

I think you would have to add the data to your new redirected request since I think redirect will truly make a new request (not forward the current one)

Yes sorry that’s a typo. I have set the class like:

<?php

namespace App\Form;

use Cake\Form\Form;
use Cake\Form\Schema;
use Cake\Validation\Validator;

class OneForm extends Form

and in OrdersController:

<?php
declare(strict_types=1);

namespace App\Controller;

use App\Form\OneForm;

...
public function add()
    {
		$oneBigForm=new OneForm();

that looks right. Does it work now?

No, I hadn’t changed anything before responding, it is the same code as two days ago to which you responded :slight_smile:

I probably misused the word redirection, I meant the routing from add() to index(). However, the data was actually requested and debug() was indeed not showing output. I created a new post for this specific issue here Debug() visibility depending on following debug(e.g. $this)

You may have correctly used the word redirection, and are now misusing “routing”. :slight_smile: Routing is basically what figures out which controller action to call for a particular URL; redirection is the act of sending the visitor to a new URL after completing some process.

Redirecting to index is normal after add, but you haven’t shown any code that includes a redirect call. And this may explain your other “bug”: if you’re calling debug and then redirect, then the output from the debug call will be lost forever. Debug output is not like flash text, which persists in the session between page loads.