CakePHP 4 Dynamic Input and Save Multiple Entities

When developing a web application, a dynamic input usually will be used to complete some of the tasks such as dynamically insert data or bulk data. Using jQuery, the dynamic input can be easily generated even there are lots of web articles/tutorials has been published regarding this matter but this tutorial was specifically focused on generating dynamic input for CakePHP 4 and save it into the database. The focus of this tutorial is to create the dynamic input field and save the multiple entities.


Insert jQuery
Load the jQuery in your template file/view. Recommended to use CDN:

<?= $this->Html->script('https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js'); ?>


The Dynamic Input Form
In this tutorial, the dynamic data is the users' data which include i) fullname; ii) username; iii) email; iv) password; v) role; vi) status and; vii) verified. This process is used to bulk register a user account where each of the aforementioned data is required. Create a form known as bulk_insert.php in …templates/Users which contain the following codes:

<?= $this->Form->create() ?>
	<table class="table table-bordered" id="dynamic_field">  
		<tr>
			<th>#</th>
			<th>Fullname</th>
			<th>Username</th>
			<th>Email</th>
			<th>Password</th>
			<th>Role</th>
			<th>Status</th>
			<th>Verified</th>
			<th>Action</th>
		</tr>
		<tr>  
			 <td>1</td> 
			<td>
			 <?php echo $this->Form->control('0.user_detail.fullname', ['class' => 'form-control','required' => false,'label' => false, 'type' => 'text', 'placeholder' => 'Fullname', 'id' => '0-user-detail-fullname']); ?>
			 </td> 					 
			 <td>
			 <?php echo $this->Form->control('0.username', ['class' => 'form-control','required' => false,'label' => false, 'type' => 'text', 'placeholder' => 'Username', 'id' => '0-username']); ?>
			 </td>  
			 <td>
			 <?php echo $this->Form->control('0.email', ['class' => 'form-control','required' => false,'label' => false, 'type' => 'text', 'placeholder' => 'Email', 'id' => '0-email']); ?>
			 </td>  
			 <td>
			 <?php echo $this->Form->control('0.password', ['class' => 'form-control','required' => false,'label' => false, 'type' => 'password', 'placeholder' => 'Password', 'id' => '0-password']); ?>
			 </td>  
			 <td>
			 <?php echo $this->Form->control('0.user_group_id', ['options' => $userGroups, 'default' => '3', 'empty' => false, 'class' => 'form-control','required' => false,'label' => false, 'id' => '0-user-group-id']); ?>
			 </td>
			 <td>
		<div>
			<?php echo $this->Form->checkbox('0.status', [
				'class' => 'form-control',
				'type' => 'checkbox']); ?>
		</div>
			 </td>  
			 <td>
		<div>
			<?php echo $this->Form->checkbox('0.is_email_verified', [
				'class' => 'form-control',
				'type' => 'checkbox']); ?>
		</div>
			 </td> 
			 <td><button type="button" name="add" id="add" class="btn btn-outline-primary btn-flat"><i class="fas fa-plus"></i></button></td>  
		</tr>  
	</table> 
	
<?= $this->Form->button(__('Submit'),['class' => 'btn btn-outline-primary btn-flat']) ?>
<?= $this->Form->end() ?>


Did you notice the field naming convention? Any dots in your field names will be converted into nested request data. This is very important when you want to save multiple entities at once.

The Dynamic Input Script
Include the following script at the end of your input form.

<script>  
 $(document).ready(function(){  
      var i=1;  
      $('#add').click(function(){  
           i++;  
           $('#dynamic_field').append(
			'<tr id="row'+i+'">'+
			'<td>'+i+'</td>'+
			'<td><?php echo $this->Form->control("'+i+'.user_detail.fullname",["class" => "form-control","required" => false,"label" => false, "type" => "text", "placeholder" => "Fullname"]); ?></td>'+
			'<td><?php echo $this->Form->control("'+i+'.username",["class" => "form-control","required" => false,"label" => false, "type" => "text", "placeholder" => "Username"]); ?></td>'+
			'<td><?php echo $this->Form->control("'+i+'.email",["class" => "form-control","required" => false,"label" => false, "type" => "text", "placeholder" => "Email"]); ?></td>'+
			'<td><?php echo $this->Form->control("'+i+'.password",["class" => "form-control","required" => false,"label" => false, "type" => "password", "placeholder" => "Password"]); ?></td>'+
			'<td><?php echo $this->Form->control("'+i+'.user_group_id", ["options" => $userGroups, "default" => "3", "empty" => false, "class" => "form-control","required" => false,"label" => false]); ?></td>'+
			'<td><?php echo $this->Form->checkbox("'+i+'.status", ["class" => "form-control","type" => "checkbox"]); ?></td>'+
			'<td><?php echo $this->Form->checkbox("'+i+'.is_email_verifiedx", ["class" => "form-control","type" => "checkbox"]); ?></td>'+
			'<td><button type="button" name="remove" id="'+i+'" class="btn btn-outline-danger btn-flat btn_remove"><i class="fas fa-times"></i></button></td>'+
			'</tr>'
			);  
      });  
      $(document).on('click', '.btn_remove', function(){  
           var button_id = $(this).attr("id");   
           $('#row'+button_id+'').remove();  
      });  
 
 });  
</script>


The Controller
The following codes is to process the dynamic form and save it into the database using saveMany method as shown below.

public function bulkRegister()
{
	$users = $this->getTableLocator()->get('Users');
	$entities = $users->newEntities($this->request->getData());
	
	foreach ($entities as $entity) {
		if ($users->saveMany($entities)) {
			$this->Flash->success(__('The users has been saved.'));
			return $this->redirect(['action' => 'index']);
		}
		$this->Flash->error(__('The users could not be saved. Please, try again.'));
	}
	$this->set(compact('users'));
}


The following figure shows the dynamic input form.

That all. Happy coding :)