yii2-dynamicform icon indicating copy to clipboard operation
yii2-dynamicform copied to clipboard

Multiple nested widget example

Open wennaspeedy opened this issue 9 years ago • 31 comments

It's possible to send me any example of deeper scenario with mutiple nested widget?

"Here's could be hypothetical scenario: A PERSON can have many HOUSES, each house, can have multiple ROOMS."

Thank you.

wennaspeedy avatar Apr 17 '15 12:04 wennaspeedy

+1 one more example.. Thank you.

s-filko avatar Apr 18 '15 19:04 s-filko

+1 My problem is when pressing add for the second level widget it add the first within. How can i separate forms. ?

dina-samy avatar Apr 19 '15 13:04 dina-samy

Try to use different values for 'widgetBody' parameter in parent and child widgets Also, take unique values for 'insertButton' and 'deleteButton' for each child widget (i use foreach index)

s-filko avatar Apr 19 '15 13:04 s-filko

Thank you for your prompt reply. Much appreciated

dina-samy avatar Apr 19 '15 13:04 dina-samy

I hope wbraganca helps us.

wennaspeedy avatar Apr 19 '15 18:04 wennaspeedy

did any find a it out how to do?

skeeran avatar Apr 24 '15 09:04 skeeran

I don't think its supported in that manner. Otherwise we would know by now. I hope I'm wrong though :)

kaarleenzz avatar Apr 24 '15 09:04 kaarleenzz

image

thats the picture he uploaded... so it musst be possible :D

skeeran avatar Apr 24 '15 09:04 skeeran

this image isnt our scenario :(. really, look at it again...

wennaspeedy avatar Apr 27 '15 10:04 wennaspeedy

is it possible with this widget to have Multiple nested inputs ???

skeeran avatar Apr 27 '15 13:04 skeeran

Yes, is it possible @skeeran. I apologize for still have not added in the documentation an example of how to do this. I'll try to work on it this week.

wbraganca avatar Apr 27 '15 14:04 wbraganca

okay cool Thank you. I am eagerly waiting for this example.

skeeran avatar Apr 27 '15 14:04 skeeran

'dynamic-form']); ?>

is it possible to have multitple "dynamic-form" in the same view

jgrasta avatar May 02 '15 09:05 jgrasta

I am also interested if I can have more dynamic forms in the same form.. If someone can help me, I would appreciate it!

cratemos666 avatar May 17 '15 16:05 cratemos666

@wbraganca can you show us the code used in the 'Demo 3 - (Nested Dynamic Form)'?

kaarleenzz avatar May 26 '15 09:05 kaarleenzz

i am waiting for this example code over 3 months... will it be ever given ?

skeeran avatar Jun 04 '15 06:06 skeeran

waiting for the example - please post

cloudcaptain avatar Jun 09 '15 02:06 cloudcaptain

Suggest closing this issue - reference Wiki. https://github.com/wbraganca/yii2-dynamicform/wiki/Nested-Forms-Example

cloudcaptain avatar Jun 29 '15 06:06 cloudcaptain

I'm having a problem with this solution. In my case then I change something in nested form (in my case is Program and ProgramItem) fe remove or add ProgramItem, the script adds empty record for each Program.

I check my code couple of time to find out if I missed something from the tutorial, but failed to find anthnig odd.

Here's the code.

    public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        $this->oldFiles = $model->getImageFiles();
        $this->oldFileNames = $model->getImageFileNames();
        // retrieve existing Payment data
        $oldProgramIds = Program::find()->select('id')->asArray()->all();
        $oldProgramIds = ArrayHelper::getColumn($oldProgramIds,'id');
        $modelsProgram = Program::findAll(['id' => $oldProgramIds]);
        $modelsProgram = (empty($modelsProgram)) ? [new Program()] : $modelsProgram;

        // retrieve existing Loads data
        $oldProgramItemIds = [];
        foreach ($modelsProgram as $i => $modelProgram) {
            $oldItems = ProgramItem::findAll(['program_id' => $modelProgram->id]);
            $modelsProgramItem[$i] = $oldItems;
            $oldProgramItemIds = array_merge($oldProgramItemIds,ArrayHelper::getColumn($oldItems,'id'));
            $modelsProgramItem[$i] = (empty($modelsProgramItem[$i])) ? [new ProgramItem()] : $modelsProgramItem[$i];
        }


        if ($model->load(Yii::$app->request->post())){

            $this->image = $model->uploadImage();
            if($this->image === false){
                $model->information_images = json_encode($this->oldFileNames);
            }

            $modelsProgram = DynamicForms::createMultiple(Program::className(),$modelsProgram);
            DynamicForms::loadMultiple($modelsProgram, Yii::$app->request->post());
            $newProgramIds = ArrayHelper::getColumn($modelsProgram,'id');

            $newProgramItemIds = [];
            $itemsData['_backendCsrf'] = Yii::$app->request->post()['_backendCsrf'];

            for($i=0;$i<count($modelsProgram);$i++){
                $itemsData['ProgramItem'] =  Yii::$app->request->post()['ProgramItem'][$i];
                $modelsProgramItem[$i] = DynamicForms::createMultiple(ProgramItem::classname(),$modelsProgramItem[$i] ,$itemsData);
                DynamicForms::loadMultiple($modelsProgramItem[$i], $itemsData);
                $newProgramItemIds = array_merge($newProgramItemIds,ArrayHelper::getColumn($itemsData['ProgramItem'],'id'));
            }

            // delete removed data

            $delItemIds = array_diff($oldProgramItemIds,$newProgramItemIds);

            if (! empty($delItemIds)) ProgramItem::deleteAll(['id' => $delItemIds]);
            $delProgramIds = array_diff($oldProgramIds,$newProgramIds);
            if (! empty($delProgramIds)) Program::deleteAll(['id' => $delProgramIds]);

            // validate all models
            $valid = $model->validate();
            $valid = Program::validateInformation($modelsProgram, $modelsProgramItem);// && $valid;

            // save deposit data
            if ($valid) {
                if ($this->saveInformation($model, $modelsProgram, $modelsProgramItem)) {
                    if($this->image !== false){
                        foreach($this->oldFiles as $of){
                            unlink($of);
                        }
                        foreach ($this->image as $i) {
                            $path = $model->getImageFile($i);
                            $i->saveAs($path);
                        }
                    }
                    Yii::$app->getSession()->setFlash('success','Zapisano wszystkie dane');
                    return $this->redirect(['update','id'=>$id]);
                }
            }
        }

        // show VIEW
        return $this->render('update2', [
            'model' => $model,
            'modelsProgram'  => $modelsProgram,
            'modelsProgramItem' => $modelsProgramItem,
        ]);
    }

I appreciate any help. I attach my screens bez-nazwy-1 bez-nazwy-2 bez-nazwy-3

adriandrozdz avatar Jul 06 '15 14:07 adriandrozdz

how should be the _controller (actionUpdate & actionCreate) if i want to have two dynamic forms in one view

epulgaron avatar Sep 11 '15 14:09 epulgaron

@cloudcaptain I'm using your solution and creation is working correctly, but the update is working partially in my case.

Let me explain using your example... I can Create a Deposit with many Payments and many Loads for each Payment (works properly). On Update, i can add some Loads for Payments, but when i try to add a new Payment and new Loads for this new Payment, i got an error Undefined offset: 2 // for example

The error occurs near this section:

            // get Slots data from POST
            $newSlotIds = [];
            $loadsData['_csrf'] =  Yii::$app->request->post()['_csrf'];
            for ($i=0; $i < count($modelsGrupos); $i++) {
                $loadsData['MissaoSlot'] =  Yii::$app->request->post()['MissaoSlot'][$i];
                // ERROR POINTS TO THE LINE BELOW
                $modelsSlots[$i] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$i], $loadsData);
                Model::loadMultiple($modelsSlots[$i], $loadsData);
                $newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));
            }

I have reviewed all my code following his example and didn't find the error. Could you help me?

My Update Action in Controller:

    public function actionUpdate($id)
    {
        // retrieve existing Missao (mission) data
        $model = $this->findModel($id);

        // retrieve existing Grupos (groups) data
        $oldGrupoIds = MissaoGrupo::find()->select('id')->where(['missao_id' => $id])->asArray()->all();
        $oldGrupoIds = ArrayHelper::getColumn($oldGrupoIds,'id');
        $modelsGrupos = MissaoGrupo::findAll(['id' => $oldGrupoIds]);
        $modelsGrupos = (empty($modelsGrupos)) ? [new MissaoGrupo] : $modelsGrupos;

        // retrieve existing Slots data
        $oldSlotIds = [];
        foreach ($modelsGrupos as $i => $modelGrupo) {
            $oldSlots = MissaoSlot::findAll(['grupo_id' => $modelGrupo->id]);
            $modelsSlots[$i] = $oldSlots;
            $oldSlotIds = array_merge($oldSlotIds, ArrayHelper::getColumn($oldSlots, 'id'));
            $modelsSlots[$i] = (empty($modelsSlots[$i])) ? [new MissaoSlot()] : $modelsSlots[$i];
        }

        // handle POST
        if ($model->load(Yii::$app->request->post())) {

            // Pega os Grupos via POST
            $modelsGrupos = Model::createMultiple(MissaoGrupo::classname(), $modelsGrupos);
            Model::loadMultiple($modelsGrupos, Yii::$app->request->post());
            $newGrupoIds = ArrayHelper::getColumn($modelsGrupos, 'id');

            // get Slots data from POST
            $newSlotIds = [];
            $loadsData['_csrf'] =  Yii::$app->request->post()['_csrf'];
            for ($i=0; $i < count($modelsGrupos); $i++) {
                $loadsData['MissaoSlot'] =  Yii::$app->request->post()['MissaoSlot'][$i];
                $modelsSlots[$i] = Model::createMultiple(MissaoSlot::classname(), $modelsSlots[$i], $loadsData);
                Model::loadMultiple($modelsSlots[$i], $loadsData);
                $newSlotIds = array_merge($newSlotIds, ArrayHelper::getColumn($loadsData['MissaoSlot'], 'id'));
            }

            // delete removed data
            $delSlotIds = array_diff($oldSlotIds, $newSlotIds);
            if (! empty($delSlotIds)) MissaoSlot::deleteAll(['id' => $delSlotIds]);
            $delGrupoIds = array_diff($oldGrupoIds, $newGrupoIds);
            if (! empty($delGrupoIds)) MissaoGrupo::deleteAll(['id' => $delGrupoIds]);

            // validate all models
            $valid = $model->validate();
            $valid = $this->validaMissao($modelsGrupos, $modelsSlots) && $valid;

            if ($valid) { 
                if ($this->saveMissao($model, $modelsGrupos, $modelsSlots)) {
                    return $this->redirect(['view', 'id' => $model->id]);
                }
            }
        }

        return $this->render('update', [
            'model' => $model,
            'modelsGrupos' => $modelsGrupos,
            'modelsSlots' => $modelsSlots,
        ]);
    }

Sorry for my bad english!

EDIT (SOLVED)

http://stackoverflow.com/questions/32585832/yii-2-nested-forms

jonatasfl avatar Sep 14 '15 18:09 jonatasfl

plz let me see your validaMissao function

epulgaron avatar Sep 22 '15 14:09 epulgaron

Of course:

public static function validaMissao($modelsGrupos, $modelsSlots)
{
        $valid = true;
        foreach ($modelsGrupos as $i => $modelGrupo) {
                $valid = $modelGrupo->validate() && $valid;
                $valid = Model::validateMultiple($modelsSlots[$i]) && $valid;
        }

        return $valid;
 }

jonatasfl avatar Sep 22 '15 15:09 jonatasfl

I solved the question above with StackOverflow help: http://stackoverflow.com/questions/32585832/yii-2-nested-forms

jonatasfl avatar Sep 22 '15 15:09 jonatasfl

thx i solved actionCreate, but now I've been following the example for actionUpdate and i get this when i click on the update button

PHP Warning – yii\base\ErrorException

Invalid argument supplied for foreach()

1. in C:\wamp\www\advanced\vendor\yiisoft\yii2\helpers\BaseArrayHelper.php at line 371
362363364365366367368369370371372373374375376377378379380     * @param array $array
     * @param string|\Closure $from
     * @param string|\Closure $to
     * @param string|\Closure $group
     * @return array
     */
    public static function map($array, $from, $to, $group = null)
    {
        $result = [];
        foreach ($array as $element) {
            $key = static::getValue($element, $from);
            $value = static::getValue($element, $to);
            if ($group !== null) {
                $result[static::getValue($element, $group)][$key] = $value;
            } else {
                $result[$key] = $value;
            }
        }

could someone help me?

epulgaron avatar Sep 24 '15 20:09 epulgaron

Please post your actionUpdate code and i'll try to help you.

jonatasfl avatar Sep 25 '15 00:09 jonatasfl

public function actionUpdate($id)
    {
        $model = $this->findModel($id);
        $modelsModelo01 = $model->modelo01;
        $modelsModelo03 = $model->modelo03s;
        $modelsModelo04 = $model->modelo04s;

        // retrieve existing Modelo05 data
        $oldModelo05Ids = Modelo05::find()->select('planificacion_fk')
            ->where(['planificacion_fk' => $id])->asArray()->all();
        $oldModelo05Ids = ArrayHelper::getColumn($oldModelo05Ids,'planificacion_fk');
        $modelsModelo05 = Modelo05::findAll(['planificacion_fk' => $oldModelo05Ids]);
        $modelsModelo05 = (empty($modelsModelo05)) ? [new Modelo05] : $modelsModelo05;

         // retrieve existing Implicados data
        $oldImplicadosIds = [];
        foreach ($modelsModelo05 as $i => $modelModelo05) {
            $oldImplicados = Implicados::findAll(['modelo05_fk' => $modelModelo05->modelo05_no_registro]);
            $modelsImplicados[$i] = $oldImplicados;
            $oldImplicadosIds = array_merge($oldImplicadosIds,ArrayHelper::getColumn($oldImplicados,'modelo05_fk'));
            $modelsImplicados[$i] = (empty($modelsImplicados[$i])) ? [new Implicados] : $modelsImplicados[$i];
        }


        if ($model->load(Yii::$app->request->post()) ) 
        {
            //modelo01
            $oldIDs = ArrayHelper::map($modelsModelo01, 'planificacion_fk', 'planificacion_fk');
            $modelsModelo01 = Model::createMultiple(Modelo01::classname(), $modelsModelo01);
            Model::loadMultiple($modelsModelo01, Yii::$app->request->post());
            $deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsModelo01, 'planificacion_fk', 'planificacion_fk')));

            //modelo03
            $oldIDs1 = ArrayHelper::map($modelsModelo03, 'planificacion_fk', 'planificacion_fk');
            $modelsModelo03 = Model::createMultiple(Modelo03::classname(), $modelsModelo03);
            Model::loadMultiple($modelsModelo03, Yii::$app->request->post());
            $deletedIDs1 = array_diff($oldIDs1, array_filter(ArrayHelper::map($modelsModelo03, 'planificacion_fk', 'planificacion_fk')));

            //modelo04
            $oldIDs2 = ArrayHelper::map($modelsModelo04, 'planificacion_fk', 'planificacion_fk');
            $modelsModelo04 = Model::createMultiple(Modelo04::classname(), $modelsModelo04);
            Model::loadMultiple($modelsModelo04, Yii::$app->request->post());
            $deletedIDs2 = array_diff($oldIDs2, array_filter(ArrayHelper::map($modelsModelo04, 'planificacion_fk', 'planificacion_fk')));

            //modelo05
            $modelsModelo05 = Model::createMultiple(Modelo05::classname(), $modelsModelo05);
            Model::loadMultiple($modelsModelo05, Yii::$app->request->post());
            $newModelo05Ids = ArrayHelper::getColumn($newModelo05Ids,'planificacion_fk');

            // implicados
            $newImplicadosIds = [];
            $implicadosData['_csrf'] =  Yii::$app->request->post()['_csrf'];
            for ($i=0; $i<count($modelsModelo05); $i++) {
                $implicadosData['Implicados'] =  Yii::$app->request->post()['Implicados'][$i];
                $modelsImplicados[$i] = Model::createMultiple(Implicados::classname(),$modelsImplicados[$i] ,$implicadosData);
                Model::loadMultiple($modelsImplicados[$i], $implicadosData);
                $newImplicadosIds = array_merge($newImplicadosIds,ArrayHelper::getColumn($implicadosData['Implicados'],'modelo05_fk'));
            }

            // delete removed data
            $delImplicadosIds = array_diff($oldImplicadosIds,$newImplicadosIds);
            if (! empty($delImplicadosIds))
            {
                Implicados::deleteAll(['modelo05_fk' => $delImplicadosIds]);
            }
            $delModelo05Ids = array_diff($oldModelo05Ids,$newModelo05Ids);
            if (! empty($delModelo05Ids))
            {
                Modelo05::deleteAll(['planificacion_fk' => $delModelo05Ids]);            
            }           

            // validate all models
            $valid = $model->validate();
            $valid = Model::validateMultiple($modelsModelo01) && $valid;
            $valid1 = $model->validate();
            $valid1 = Model::validateMultiple($modelsModelo03) && $valid1;
            $valid2 = $model->validate();
            $valid2 = Model::validateMultiple($modelsModelo04) && $valid2;
            $valid3 = $model->validate();
            $valid3 = Modelo05::validatePlanificacion($modelsModelo05,$modelsImplicados) && $valid3;


            if ($valid && $valid1 && $valid2 && $valid3) {
                $transaction = \Yii::$app->db->beginTransaction();
                try {
                    if ($flag = $model->save(false)) {
                        if (! empty($deletedIDs) && ! empty($deletedIDs1) && ! empty($deletedIDs2)) {
                            Modelo01::deleteAll(['planificacion_fk' => $deletedIDs]);
                            Modelo03::deleteAll(['planificacion_fk' => $deletedIDs1]);
                            Modelo04::deleteAll(['planificacion_fk' => $deletedIDs2]);
                        }
                        foreach ($modelsModelo01 as $modelModelo01) {
                            $modelModelo01->planificacion_fk = $model->plan_cod;
                            if (! ($flag = $modelModelo01->save(false))) {
                                $transaction->rollBack();
                                break;
                            }
                        }
                        foreach ($modelsModelo03 as $modelModelo03) {
                            $modelModelo03->planificacion_fk = $model->plan_cod;
                            if (! ($flag = $modelModelo03->save(false))) {
                                $transaction->rollBack();
                                break;
                            }
                        }
                        foreach ($modelsModelo04 as $modelModelo04) {
                            $modelModelo04->planificacion_fk = $model->plan_cod;
                            if (! ($flag = $modelModelo04->save(false))) {
                                $transaction->rollBack();
                                break;
                            }
                        }
                        foreach ($modelsModelo05 as $i => $modelModelo05) {
                            // save the payment record
                            $modelModelo05->planificacion_fk = $model->plan_cod;
                            if ($flag = $modelModelo05->save(false)) {
                                // loop through each load
                                foreach ($modelsImplicados[$i] as $ix => $modelImplicados) {
                                    // save the load record
                                    $modelImplicados->modelo05_fk = $modelModelo05->modelo05_no_registro;
                                    if (! ($flag = $modelImplicados->save(false))) {
                                        $transaction->rollBack();
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    if ($flag) {
                        $transaction->commit();
                        return $this->redirect(['view', 'id' => $model->plan_cod]);
                    }
                } catch (Exception $e) {
                    $transaction->rollBack();
                }
            }
        } else {
            return $this->render('update', [
                'model' => $model,
                'modelsModelo01' => (empty($modelsModelo01)) ? [new Modelo01] : $modelsModelo01,
                'modelsModelo03' => (empty($modelsModelo03)) ? [new Modelo03] : $modelsModelo03,
                'modelsModelo04' => (empty($modelsModelo04)) ? [new Modelo04] : $modelsModelo04,
                'modelsModelo05' => (empty($modelsModelo05)) ? [new Modelo05] : $modelsModelo05,
                'modelsImplicados' => (empty($modelsImplicados)) ? [new Implicados] : $modelsImplicados
            ]);
        }
    }

epulgaron avatar Sep 25 '15 15:09 epulgaron

Guys, I had the same issue here: i have a form with two nested forms in it, and the second form's add button appends a first nested form's item, I solved it changing the widgetContainer param in DynamicFormWidget initialization.

Hope it helps!

asaenzestrada avatar Dec 05 '15 15:12 asaenzestrada

@asaenzestrada its works thx a lot, but now actionUpdate give me this error PHP Warning – yii\base\ErrorException

array_combine(): Both parameters should have an equal number of elements

this happends here $modelsModelo03 = Model::createMultiple(Modelo03::classname(), $modelsModelo03); anytime that i call createMultiple in my actionUpdate i get the same

epulgaron avatar Feb 22 '16 19:02 epulgaron

@epulgaron You have to adjust your controller to process the nested forms. Use Xdebug to check the values on execution and find data you need in \Yii::$app->request->post(), there is a multidimensional array with the form data, You should load it to your models.

asaenzestrada avatar Feb 22 '16 20:02 asaenzestrada