[EXTENSION] multimodelform/jqrelcopy

Issue resolved, some problem in my code.

Thanks

Mr Joblo, do you know of a better way to pass hidden values via multimodel form?

This is what I’m using but it will only work after I get an error 500 then go “back” and then resend the data:


	

  	$listFormConfig = array(

  	'elements'=>array(

    	'alive'=>array(

		'type'=>'hidden',

		'value'=>'0',

    	),

		'dead'=>array(

		'type'=>'hidden',

		'value'=>'0',

    	),

));

		

For clarification, these are supposed to be checkboxs, but I want default value to be "0" or "not checked". Not sure how to do this with multimodelform.

Want to update this, I have made a ‘workaround’ for this. I simply make the scope fields required input so the error is not shown.

Hi Joblo,

I am using two dropdownlist in FormConfig in which one dropdownlist depends on another. But when i make a copy of it, the dependent dropdownlist shows the same values for all copies irespective of what value is selected in the first dropdownlist for that copy.

i.e. suppose i select city in first dropdownlist, the second dropdownlist shows its respective values but when i make a copy of it the second dropdown shows the same values(city values) irrespective of what is selected in the first dropdownlist.

My code

In view




'elements'=>array(

        'parameter1'=>array(

            'type'=>'dropdownlist',

            'items'=>CHtml::listData(Parameters::model()->findAll(array('order'=>'parameter_name','condition'=>'Module_List_id=1')),'parameter_name','parameter_name'),

			'prompt'=>'--please select--',

			'ajax'=>array(

							'type'=>'POST',

							'url'=>CController::createUrl('RuleList/dynamicvalues'),

							'update'=>'#'.CHtml::activeId($rulefrontend,'parameter2')

							)       

		),



In controller




public function actionDynamicvalues()

	{

		$i=0;

		$para=$_POST['RuleFrontend']['parameter1'][((int)$i)];

		//echo $para;

		$para_id=Parameters::model()->findAllBySql("select parameter_id from parameters where parameter_name='".$para."'");

							$para_id=CHtml::listData($para_id,'parameter_id','parameter_name');

							foreach($para_id as $value=>$parameter)  

							{

								$parameter_id=CHtml::encode($value);

							}

		$data=ConditionParameter::model()->findAll('parameter_id=:parameter_id', 

                  array(':parameter_id'=>$parameter_id));

		$data=CHtml::listData($data,'cond_parameter_id','cond_parameter');

		foreach($data as $value=>$cond_parameter)  

		{

                echo CHtml::tag('option',array('value'=>CHtml::encode($cond_parameter)),CHtml::encode($cond_parameter),true);

		}

		

		

	}



Thanks

I think it’s not possible or not so easy to implement with multimodelform/jqrelcopy.

There only happens a clientside cloning of dom-elements and these elements don’t know anything about ajax …

That’s the reason why cloning a simple datetimepicker needs a javascript workaround too (see documentation).

JQRelCopy by default can only handle simple elements to clone.

So you have to change the code of the jquery.relcopy.js script if you need such a special solution.

Take a look at the js-code what happens on cloning.

Maybe you can place an ajax-call there to retrieve and display the values you need…

Another technique to handle multiple elements in a form is described in the book of ‘samdark’:

Yii 1.1 Application Development Cookbook

He uses ajax calls, creates empty models at the server and delivers the form fields to the client as response.

See Chapter 3: Handling variable number of inputs.

Maybe this suits better for your needs than multimodelform.

Hi Joblo

First, thanks for your great extension… But I got some problems when I tried to implement it.

The tables I used for testing it is not same as your example in Extension Page–which is, Group and Member–my tables is Orders and Orderdetails. But the rest of code is same, with some modifications in variable names.

I’ve got an error thrown and the picture is attached:2185

PHP Error 2011-10-28 08-44-09.png

I use the last version of MultiModelForm (2.2.1)

Seems to be a bug

Add "if(!empty($refArray))" above the foreach in line 437:




if (!empty($refArray)) //<- add this

  foreach($refArray as $idx => $value)

  {

	// check continue if all values are empty

	if (empty($value))

	{

           ...



Please let me know if this solves the problem.

Thanks joblo for your reply, but…

I’ve checked the $refArray and follow your suggestion, but the $refArray is not empty. I’ve debugged this code with NetBeans (with XDebug installed) and I found that the value of $refArray is not an array, it’s an integer (the value of the first field ID (Primary Key) of my OrderDetails (the details side) table). Maybe because of the return value of array_shift() method?

Ok, I will explain my situation in more details:

My Master-Details tables is Orders and Orderdetails. The relationship between these tables is made by the Foreign Key column in Orderdetails: OrderID (it’s an integer, but not autoincrement value, of course). This Orderdetails table have a Primary Key field ID, which is an autoincrement integer value from MySQL. The Orders itself have a Primary Key ID column, which is autoincremented too.

The problems that I’ve showed to you was happen when I do an Update without modifying any values in Orders fields nor Orderdetails fields.

This multimodelform extension seems to be a promising extension for me, because I think it’s very difficult to implement it in Yii for real CRUD conditions. I hope you can help me to solve this problems. Thanks joblo.

Regards,

Angga

The ‘$formData’ is populated by $_POST.

What is the content of $_POST when assigned to formdata at the beginning of the function initItems?

Does the form submit correct POST-values (formData[$modelClass])?




if (!isset($formData))

   $formData = $_POST;


var_dump($formData);




On update it should contain at least two arrays:

The item(s) to update and an empty item added by default - take a look at the source-html (Member[firstname][] … in the demo).

So what is the content of $formData[$modelClass] before array_shift and the $refArray at the lines above?




  var_dump($formData[$modelClass]); 


  $refAttribute = key($formData[$modelClass]);

  $refArray = array_shift($formData[$modelClass]);


  var_dump($refArray); 

 



Can you send me a mail with your full example code including db?

MY CODE IS WRONG, AND PLEASE APOLOGIZE ME JOBLO… :(

My mistakes is in line 78 (see the Stack Trace from my attached picture previously). I write $model in the calling of MultiModelForm::save(), instead of $orderDetails (the details-side of model).

BIG, BIG THANKS for your responses of my stupid questions… This is why I’m a newbie. But your last reply remind me that the mistakes might be in my code.

AND… The Update is run well… BUT… The Create operation is still stucked…

I hope you would help once again ( ;D ): How can I create a new Order with a few new OrderDetails if the ID (Primary Key) of Order is autoincremented and the OrderDetails Foreign Key OrderID is originated from that ID?

It should be implemented like the example ‘actionCreate’ in the documentation of the extension.

  • validate the orderdetails and save the order model

  • if this is ok the order model id attribute should be populated with the new (autoincremented) id

  • set: $masterValues = array (‘OrderID’=>$model->id);

  • save the detail records: MultiModelForm::save($detailModel,$validatedItems,$deleteItems,$masterValues)).

    The masterValues will be assigned to the detail records on insert/save.

This is my actionCreate() method:


public function actionCreate()

{

    Yii::import('ext.multimodelform.MultiModelForm');


    $model = new Orders;


    $orderDetails = new Orderdetails;

    $validatedOrderdetails = array();

    $deleteOrderdetails = array();


    // Uncomment the following line if AJAX validation is needed

//		$this->performAjaxValidation($model);


    if (isset($_POST['Orders']))

    {

        $model->attributes = $_POST['Orders'];


        if (MultiModelForm::validate($orderDetails, $validatedOrderdetails, $deleteOrderdetails) && 

                $model->save())

        {                

            $masterValues = array('OrderID' => $model->ID);


            if (MultiModelForm::save($orderDetails, $validatedOrderdetails, $deleteOrderdetails, $masterValues))

                $this->redirect(array('view', 'id' => $model->ID));

        }

    }


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

        'model' => $model,

        'orderDetails' => $orderDetails,

        'validatedOrderdetails' => $validatedOrderdetails,

    ));

}

But it still doesn’t do the creation. (Maybe I made another typos mistakes?)

Can’t see a mistake.

You have to ‘var_dump’ after each step to see what’s going on.

Is the master model saved …





 if (isset($_POST['Orders']))

    {

        $model->attributes = $_POST['Orders'];

-> Do you reach this code?<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />?<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />???

var_dump($model);


        if (MultiModelForm::validate($orderDetails, $validatedOrderdetails, $deleteOrderdetails) && 

                $model->save())

        {                

            

-> Do you reach this code?<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />?<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />???

var_dump($model);

var_dump($validatedOrderdetails); 

                 $masterValues = array('OrderID' => $model->ID);


            if (MultiModelForm::save($orderDetails, $validatedOrderdetails, $deleteOrderdetails, $masterValues))

 -> Do you reach this code?<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />?<img src='http://www.yiiframework.com/forum/public/style_emoticons/default/huh.gif' class='bbc_emoticon' alt='???' />???               


           $this->redirect(array('view', 'id' => $model->ID));

        }

    }






When I tested my create operation in my browser: I fill-in all the required fields, and hit save, the errorSummary section showed up a warning “OrderID cannot be blank”. This OrderID means the foreign key in the OrderDetails side (in the MultiModelForm). The ID textField of Orders itself is not displayed because it’s generated by Gii (it’s an autoincremented field), so I’m not displayed the OrderID field in the MultiModelForm too.

Then I tried to change my code:




public function actionCreate()

{

	Yii::import('ext.multimodelform.MultiModelForm');

	

	$model = new Orders;

	

	$orderDetails = new Orderdetails;

	$validatedOrderdetails = array();

	$deleteOrderdetails = array();


	// Uncomment the following line if AJAX validation is needed

//		$this->performAjaxValidation($model);

	

	if (isset($_POST['Orders']))

	{

		$model->attributes = $_POST['Orders'];

		

		/////////////////////////////////////////////////////

		if ($model->save())

		/////////////////////////////////////////////////////

		{                                

			$masterValues = array('OrderID' => $model->ID);


			if (MultiModelForm::save($orderDetails, $validatedOrderdetails, $deleteOrderdetails, $masterValues))

				$this->redirect(array('view', 'id' => $model->ID));

		}

	}


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

		'model' => $model,

		'orderDetails' => $orderDetails,

		'validatedOrderdetails' => $validatedOrderdetails,

	)); 

}



And it works. But I think with this new code the Orders can be created without containing any OrderDetails, and it’s not the behaviour I expected.

Now I understand the problem:

The detail needs a OrderID on validation, but there is none, because the master order is not saved.

This should work, if you don’t want to save a order without details:





public function actionCreate()

{

        Yii::import('ext.multimodelform.MultiModelForm');

        

        $model = new Orders;

        

        $orderDetails = new Orderdetails;

        $validatedOrderdetails = array();

        $deleteOrderdetails = array();


        // Uncomment the following line if AJAX validation is needed

//              $this->performAjaxValidation($model);

        

        if (isset($_POST['Orders']))

        {

                $model->attributes = $_POST['Orders'];

                

               

                //build a 'dummy' $masterValues for validation only so that OrderID is not blank

                $masterValues = array('OrderID' => 0); 


   //submit the $masterValues on validate too           

   if (MultiModelForm::validate($orderDetails, $validatedOrderdetails, $deleteOrderdetails,$masterValues) && 

                    $model->save())

                {                                

                        $masterValues = array('OrderID' => $model->ID); //here you use the correct FK for saving


                        if (MultiModelForm::save($orderDetails, $validatedOrderdetails, $deleteOrderdetails, $masterValues))

                                $this->redirect(array('view', 'id' => $model->ID));

                }

        }


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

                'model' => $model,

                'orderDetails' => $orderDetails,

                'validatedOrderdetails' => $validatedOrderdetails,

        )); 

}




Thanks a lot, Joblo.

You’ve save my life… Now it works…

I didn’t know that I just only have to write


$masterValues = array('OrderID' => 0); 

to fix my problems

Hi,

I am looking for an extension that works with the following scenario:

Order - Products : MANY_MANY relation




'products' => array(self::MANY_MANY,Products , ‘tbl_order_products(order_id, product_id)'),



Order - Categories : MANY_MANY relation




'categorias' => array(self::MANY_MANY,Categories , ‘tbl_order_categories(order_id, category_id)'),



Database Model




tbl_order

 - id

 - order_desc


tbl_products

 - id

 - product_desc


tbl_categories

 - id

 - category_desc


tbl_order_products

 - order_id

 - product_id

 - product_quantity


tbl_order_categories

 - order_id

 - product_id






Now I want to create a single form to manage all of this at once:

i) Create an order (general order fields)

ii) Add as many products as user wants to the order (loaded by Products model) and define the quantity of each product

iii) Pick the categories matching with the order

For ii) the user must be able to add items dynamically using JQuery relcopy feature.

As iii) I want all the categories of Categories model to be listed in the form for selection in a checkbox format.

Can I use multimodelform to do ii) and iii) together? If so can you please give me some guidance?

By the way giix capabilities will make iii) job for me… But for ii) it I am afraid it won’t as there is a product_quantity to manage…

Thanks in advance for your help,

A.Miguel

More on customization:

[I think now addicted to this great extension]

But… still I’m a newbie… So, another question from me:

How can I remove the error label (not errorSummary) from MultiModelForm when validation occurs without modifying any CSS?

A.Miguel: I think it should be possible.

  1. Lets giix do the job for the form/controller for the order and the categories related to the order (checkboxes).

    Comment the tbl_order_categories before creating the CRUD with the help of giix, so that the code for OrderProduct in the form/controller will not be created.

  2. Add the code for the multimodelform (= detail = order_product) manually.

Add a ‘getMultiModelForm’ method to your OrderProduct model.

Something like this:





public function getMultiModelForm() 

{


$products = Products::model()->findAll();

$productItems = CHtml::listData($products,'id','product_desc');


return array(

      'elements'=>array(

        

        //a dropdownlist for the product

         'productid'=>array(

            'type'=>'dropdownlist',

            'items'=>$productItems ;

        ),


        //a textinput for the quantity

        'product_quantity'=>array(

            'type'=>'text',

            'maxlength'=>5,

        ),

    ));


}




  • Add the multimodelform widget in the form:

    The ‘$member’ from the documentation is your OrderProduct model.





$this->widget('ext.multimodelform.MultiModelForm',array(

        'id' => 'id_order', 

        'formConfig' => $orderProduct->getMultiModelForm(),

        'model' => $orderProduct, //instance of the form model

        'validatedItems' => $validatedOrderProduct , //not empty when not in validation mode with showing errors

 

        //test this for a better performance: need data only when not displaying errors 

        'data' => empty($validatedOrderProduct) ? $orderProduct->findAll('order_id=:orderId', array(':orderId'=>$model->id)) : null,

    ));



  • Add the multimodelform code to save/validate the multimodel data similar to the one in the documentation.

    $model = Order, $member = $orderProduct ($validatedOrderProduct …).

    Maybe try first with ‘MultiModelForm::save’ only and add ‘MultiModelForm::validate’ later.

  • Hint: do var_dump in the actionCreate to see whats comming in, will be validated …

anggara: You can do this in the form-config:




  $formConfig = array(

    'showErrorSummary' => true,  //suppress error messages for each input   


    'elements'=>array(


       ....




See: CFormInputElement.render