How to create/save more Model inputs and make them repeatable with jQuery

Ok maybe you remember my last topic about this problem: http://www.yiiframew…pic,1527.0.html

I've attached my database schema with all relations between tables.

Ok the attachment is too big so I uploaded it: http://www.ilmanicom…p/db_schema.pdf

I'm going to introduce my problem and how I tryed to resolve it.

I have four tables like you can see. I've Lastminute, Period, Description and DescriptionText.

The user can add more Periods at the same Lastminute and can add more Description to the same Lastminute. A Description can be translated in many languages using DescriptionText ( where you have the language flag, the title and text in that language ).

Anyway if you will take a look to the schema I think that you will understand the structure of the DB very fast.

Ok. When the user is creating a new Lastminute I want to allow him to enter some periods and some description related to that Lastminute.

The real problem is this: how to merge more Model inputs and make them repeatable without editing the behaviour of Yii? I don’t really found the right answer at the moment. So this is my trick :P

This is the create view of my Lastminute:



<h2>New Lastminute</h2>





<div class="actionBar">


[<?php echo CHtml::link('Lastminute List',array('list')); ?>]


[<?php echo CHtml::link('Manage Lastminute',array('admin')); ?>]


</div>








<div class="yiiForm">





	<p>


	Fields with <span class="required">*</span> are required.


	</p>


	<?php echo CHtml::form('','post',array('enctype'=>'multipart/form-data')); ?>


	


	<?php echo CHtml::errorSummary(array_merge( array($lastminute), $periods, $details)); ?>





	<div class="simple">


	<?php echo CHtml::activeLabelEx($lastminute,'prezzo'); ?>


	<?php echo CHtml::activeTextField($lastminute,'prezzo'); ?>


	</div>


	<div class="simple">


	<?php echo CHtml::activeLabelEx($lastminute,'dataScadenza'); ?>


	<?php echo CHtml::activeTextField($lastminute,'dataScadenza'); ?>


	</div>


	<div class="simple">


	


	


	<?php echo CHtml::activeLabelEx($lastminute,'immagine'); ?>


	<?php echo CHtml::activeFileField($lastminute, 'immagine'); ?>


	</div>





	<?php echo CHtml::button('add Period', array('name'=>'addPeriod', 'id'=>'addPeriod')); ?>


	


	<?php foreach($periods as $i => $period): ?>


	<div id="periodo-<?php echo $i ?>">


		


		


		<div class="simple">


		<?php echo CHtml::activeLabelEx($period,'dataInizio'); ?>


		<?php echo CHtml::activeTextField($period,"dataInizio[$i]"); ?>


		</div>


		<div class="simple">


		<?php echo CHtml::activeLabelEx($period,'dataFine'); ?>


		<?php echo CHtml::activeTextField($period,"dataFine[$i]"); ?>


		</div>


		<br />


	</div>


	<?php endforeach; ?>





	<?php echo CHtml::button('add Detail', array('name'=>'addDetail', 'id'=>'addDetail')); ?>


	


	<?php foreach($details as $i => $detail): ?>


	<div id="dettaglio-<?php echo $i ?>">


		<div class="simple">


		<?php echo CHtml::activeLabel($detail,'codiceLingua'); ?>


		<?php echo CHtml::activeDropDownList($detail,"codiceLingua[$i]",DettaglioTesto::model()->statusOptions); ?>


		</div>


		<div class="simple">


		<?php echo CHtml::activeLabelEx($detail,'titolo'); ?>


		<?php echo CHtml::activeTextField($detail,"titolo[$i]"); ?>


		</div>


		<div class="simple">


		<?php echo CHtml::activeLabelEx($detail,'descrizione'); ?>


		<?php echo CHtml::activeTextField($detail,"descrizione[$i]"); ?>


		</div>


		<br />


	</div>


	<?php endforeach; ?>


	


	<div class="row action">


	<?php echo CHtml::submitButton($update ? 'Save' : 'Create', array('name'=>'submitDatas')); ?>


	


	</div>








	</form>


</div>

















<script type="text/javascript">





	/*<![CDATA[*/


	// I need to know how many periods I've already added when the validate return FALSE


	var periodsAdded = <?php echo $periodsNumber; ?>;


	// I need to know how many details I've already added when the validate return FALSE


	var detailsAdded = <?php echo $detailsNumber; ?>;





	// Add the event to the period's add button


	$('#addPeriod').click(function () {


		// I'm going to clone the first div containing the Model input couse I don't want to create a new div and add every single structure


		var divCloned = $('#periodo-0').clone();		


		// I'm attaching the div to the last input created


		$('#periodo-'+(periodsAdded++)).after(divCloned);


		// Changin the div id


		divCloned.attr('id', 'periodo-'+periodsAdded);


		// Initializing the div contents


		initNewInputs(divCloned.children('.simple'), periodsAdded);


	});


	


	// Same things for #addPeriod


	$('#addDetail').click(function () {


		var divCloned = $('#dettaglio-0').clone();		


		$('#dettaglio-'+(detailsAdded++)).after(divCloned);


		divCloned.attr('id', 'dettaglio-'+detailsAdded);


		initNewInputs(divCloned.children('.simple'), detailsAdded);


	});


	


	function initNewInputs( divs, idNumber ) {


		// Taking the div labels and resetting them. If you send wrong information,


		// Yii will show the errors. If you than clone that div, the css will be cloned too, so we have to reset it


		var labels = divs.children('label').get();


		for ( var i in labels )


			labels[i].setAttribute('class', 'required');		


		


		// Taking all inputs and resetting them. 


		// We have to set value to null, set the class attribute to null and change their id and name with the right id.


		var inputs = divs.children('input').get();		


		


		for ( var i in inputs  ) {


			inputs[i].value = "";


			inputs[i].setAttribute('class', '');


			inputs[i].id = inputs[i].id.replace(/d+/, idNumber);


			inputs[i].name = inputs[i].name.replace(/d+/, idNumber);


		}


	}


	


	/*]]>*/


		


</script>


And this is my actionCreate in LastminuteController:



<?php


public function actionCreate()


	{


		// Adding jQuery to the view page.


		Yii::app()->getClientScript()->registerCoreScript('jquery');


		


		$lastminute = new Lastminute;


		


		// Adding an empty period and an empty Detail to the form


		$periods = array( new Periodo, );		


		$details = array( new DettaglioTesto, );


		


		$lastminute->immagine = CUploadedFile::getInstance($lastminute,'immagine');


		


		// If everything is setted I will check and process the inputs


		if( isset( $_POST['submitDatas'] ) && isset( $_POST['Periodo'], $_POST['DettaglioTesto'], $_POST['Lastminute'] ) )


		{


			$lastminute->attributes=$_POST['Lastminute'];


				


			$valid = $lastminute->validate();


			


			// This is the crappy part of the script that I need to improve and make it more elegance


			// I've to add to the periods and details array a new Period and a new Detail for every istance in $_POST['Periodo'] and $_POST['DettaglioTesto']


			foreach ( $_POST['Periodo'] as $i => $period ) {


				$periods[$i] = new Periodo;	


				if ( isset( $_POST['Periodo'][$i] ) )


					$periods[$i]->attributes = $_POST['Periodo'][$i];			


				$valid = $valid && $periods[$i]->validate();					


			}


			


			foreach ( $_POST['DettaglioTesto'] as $i => $detail ) {


				$details[$i] = new DettaglioTesto;


				if ( isset( $_POST['DettaglioTesto'][$i] ) )


					$details[$i]->attributes = $_POST['DettaglioTesto'][$i];			


				$valid = $valid && $details[$i]->validate();					


			}


				


			if( $valid && $lastminute->save() ) {


				// Resize and save the logo's image			


				$lastminute->immagine->saveAs('img/'.$lastminute->immagine->getName());


				Yii::import('application.extensions.image.Image');


				$image = new Image('img/'.$lastminute->immagine->getName());


				$image->resize(200, 200);


				$image->save();


				// Saving each period ( i've to change the date format couse in Italy we use dd/mm/yyyy


				foreach ( $periods as $i => $period ) {


					$period->lastminuteId = $lastminute->id;


					$period->dataInizio = date_format(date_create($period->dataInizio), 'Y-m-d');


					$period->dataFine = date_format(date_create($period->dataFine), 'Y-m-d');


					$period->save();


				}


				// Savin each DettaglioTesto


				foreach ( $details as $i => $detail ) {


					$parentDetail = new Dettaglio;


					$parentDetail->lastminuteId =  $lastminute->id;


					$parentDetail->save();


					$detail->dettaglioId = $parentDetail->id;


					$detail->save();


				}


				


				$this->redirect(array('show','id'=>$lastminute->id));


				


			}


		}


		$this->render('create', array(


			'lastminute' => $lastminute,


			'periods' => $periods,


	    	'details' => $details,


	    	'detailsNumber' => isset($_POST['DettaglioTesto']) ? count($_POST['DettaglioTesto'])-1 : 0, //How many Period the user added


	    	'periodsNumber' => isset($_POST['Periodo']) ? count($_POST['Periodo'])-1 : 0, //How many DettaglioTesto the user added


	    	'update' => false,


		));


	}


Is there a way to improve and optimize my code and my solution? Do you have some other good ideas? I’m here for comemnts, suggestion, dubts and critics :P

no one interested? No advices? :(

no one that need to use repetable fields? Couse I want to know if I’m taking the right way of if is there some better way :)

My idea would be similar to "collecting tabular data input" plus some client js to allow adding rows dynamically. It seems your code just implements these. Thanks for sharing!

Have I to post it in something like cookbook or tips and tutorials qiang?

Maybe I can make some easy example and not with this tables.

Waiting an answer :)

Sure. That will be wonderful!

Ok i will make a guide. I wish that my english is not so bad to make other people to understand what i’m trying to show :D