wbraganca / yii2-dynamicform

It is widget to yii2 framework to clone form elements in a nested manner, maintaining accessibility.
Other
436 stars 438 forks source link

dynamic form in modal, update fail in load-multiple index, creating error in save action #168

Open Lucianoj opened 7 years ago

Lucianoj commented 7 years ago

Using dynamicform in a modal, when add a new item in update action, the index of array content on $data array (array of arrays) start in 1 but not in 0. example:

$data = [$model, $modelContent, others];
$model = [data1, data2, otherdata]; // (ok)
$modelContent = ['1' => $item1, '2' => $item2, '3' => $item3, etc] ;//(not ok)

it must be:

$modelContent = ['0' => $item1, '1' => $item2, '2' => $item3, etc];

because $models starts with 0 index in

public static function loadMultiple($models, $data, $formName = null)

all of this, in vendor/yiisoft/yii2/base/Model.php loadMultiple function.

When use dynamic form but not in modal, it works fine.

_form.php (used in a modal)

<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;
use common\models\PermisosHelpers;
use wbraganca\dynamicform\DynamicFormWidget;
use kartik\select2\Select2;
use yii\helpers\ArrayHelper;
use frontend\models\Menu;
use frontend\models\Cliente;
use frontend\models\Mesa;
use frontend\models\EstadoVenta;
use frontend\models\CondicionVenta;
use yii\bootstrap\Modal;

/* @var $this yii\web\View */
/* @var $model frontend\models\Venta */
/* @var $form yii\widgets\ActiveForm */
?>

<?php
    $es_admin = PermisosHelpers::requerirRol('admin');
    $es_root = PermisosHelpers::requerirRol('root');
    $es_auditor = PermisosHelpers::requerirRol('auditor');
?>
<div class="venta-form">
    <?php $form = ActiveForm::begin(['id' => 'dynamic-form']); ?>
    <div class="panel panel-default">
        <div class="panel-heading"><h4 class="text-primary"><i class="fa fa-info-circle"></i> Complete los datos de la Venta </h4></div>
        <div class="panel-body">
                <div class="col col-lg-12">
                            <div class="row"><!--5-->
                                <div class="col col-lg-6">
                                    <div class="row"><!--3-->
                                        <div class="row"><!--1-->
                                            <div class="col col-lg-6">
                                                <?php 
                                                    echo $form->field($model, 'mesa_id')->widget(Select2::className(), [
                                                        'data' => ArrayHelper::map(Mesa::find()->addOrderBy('nombre')->all(), 'id', 'nombre'),
                                                        'language' => 'es',
                                                        'value' => 1,
                                                        'pluginOptions' => [
                                                            'allowClear' =>true
                                                        ]
                                                    ]); 
                                                ?>
                                            </div>
                                            <div class="col col-lg-6">
                                                <?php
                                                    echo $form->field($model, 'condicion_venta_id')->widget(Select2::className(), [
                                                        'data' => ArrayHelper::map(CondicionVenta::find()->all(), 'id', 'nombre'),
                                                        'language' => 'es',
                                                        'value' => 1,
                                                        'pluginOptions' => [
                                                            'allowClear' =>true
                                                        ],
                                                        'pluginEvents' => [
                                                            "change" => "function(e) { 
                                                                var condicion = document.getElementById('select2-venta-condicion_venta_id-container').title;
                                                                if (condicion == 'Cuenta Corriente') {
                                                                    $('#venta-cliente_id').prop('required', true);
                                                                } else {
                                                                    $('#venta-cliente_id').prop('required', false);
                                                                }
                                                                if (condicion == 'Débito') {
                                                                    $('#venta-numero_ticket_fiscal').prop('required', true);
                                                                    $('#venta-numero_ticket_fiscal').prop('focus', true);
                                                                } else {
                                                                    $('#venta-numero_ticket_fiscal').prop('required', false);
                                                                }
                                                            }",
                                                        ]
                                                    ]);
                                                ?> 
                                            </div>
                                        </div><!-- 1 -->
                                        <div class="row"><!--2-->
                                            <div class="col col-lg-12">
                                                <?php 
                                                    echo $form->field($model, 'cliente_id')->widget(Select2::className(), [
                                                        'data' => ArrayHelper::map(Cliente::findAll(['estado_id' => 1, 'estado_cuenta_corriente_id' => 1]), 'id', 'nombre'),
                                                        'language' => 'es',
//                                                        'value' => 1,
                                                        'options' => [
                                                            'placeholder' => 'Seleccione un Cliente..',
                                                        ],
                                                        'pluginOptions' => [
                                                            'allowClear' => true
                                                        ]
                                                    ]);
                                                ?>
                                            </div>
                                        </div><!--2--> 
                                    </div><!-- 3 -->
                                </div><!--col col-lg-6-->
                                <div class="row"><!--4-->
                                    <div class="col col-lg-6">
                                    <?php
                                        echo $form->field($model, 'estado_venta_id')->widget(Select2::className(), [
                                            'data' => ArrayHelper::map(EstadoVenta::find()->all(), 'id', 'nombre'),
                                            'language' => 'es',
                                            'disabled' => $bloquearEstado,
                                            'value' => 1,
                                            'pluginOptions' => [
                                                'allowClear' => true
                                            ]
                                        ]);
                                    ?>
                                    </div>
                                    <div class="col col-lg-6">
                                        <h2 class="text-primary text-center"><?= Html::encode('Total Venta: $ ')?><span id = "<?= "total" ?>" class="text-primary"></span></h2>
                                    </div><!--4-->
                                <div class="col col-lg-4">
                                    <?= $form->field($model, 'numero_ticket_fiscal')->textInput() ?>
                                </div>
                                <div class="col col-lg-8">
                                    <?= $form->field($model, 'observaciones')->textInput(['maxlength' => true]) ?>
                                </div>
                                    <?php echo $form->field($model, 'total')->hiddenInput()->label(false);//->textInput(['readOnly' => true, 'hidden' => 'yes']) ?>
                            </div><!--col col-lg-12-->

                </div><!--col col-lg-12-->
                        <div class="row">
                                <div class="panel-heading"><h4 class="text-primary"><i class="fa fa-cart-arrow-down"></i> Ítems </h4></div>
                                <div class="panel-body">

                                    <?php DynamicFormWidget::begin([
                                        'widgetContainer' => 'dynamicform_wrapper', // required: only alphanumeric characters plus "_" [A-Za-z0-9_]
                                        'widgetBody' => '.container-items', // required: css class selector
                                        'widgetItem' => '.item', // required: css class
                                        'limit' => 50, // the maximum times, an element can be cloned (default 999)
                                        'min' => 1, // 0 or 1 (default 1)
                                        'insertButton' => '.add-item', // css class
                                        'deleteButton' => '.remove-item', // css class
                                        'model' => $modelsVentaContiene[0],
                                        'formId' => 'dynamic-form',
                                        'formFields' => [
                                            //'venta_id', se debería resolver en el controlador
                                            'menu_id',
                                            'valor_venta_unitario',
                                            'cantidad',
//                                            'observaciones',
                                        ],
                                    ]); ?>

                                    <div class="container-items"><!-- widgetContainer -->
                                    <?php foreach ($modelsVentaContiene as $i => $modelVentaContiene): ?>
                                        <div class="item panel panel-default"><!-- widgetBody -->
                                            <div class="panel-heading">
                                                <h3 class="panel-title pull-left"><i class="fa fa-asterisk"></i> Ítem </h3>
                                                <div class="pull-right">
                                                    <button type="button" class="add-item btn btn-success btn-xs"><i class="glyphicon glyphicon-plus"></i></button>
                                                    <button type="button" class="remove-item btn btn-danger btn-xs"><i class="glyphicon glyphicon-minus"></i></button>
                                                </div>
                                                <div class="clearfix"></div>
                                            </div>
                                            <div class="panel-body">
                                                <?php
                                                    // necessary for update action.
                                                    if (! $modelVentaContiene->isNewRecord) {
                                                        echo Html::activeHiddenInput($modelVentaContiene, "[{$i}]id");
                                                    }
                                                ?>
                                                <div class="row">
                                                    <div class="col-sm-5">
                                                        <?php
                                                            echo $form->field($modelVentaContiene, "[{$i}]menu_id")->dropDownList($modelVentaContiene->menuLista, ['promp' => 'Seleccione ...', 'class' => 'selectMenu']);
                                                        ?>
                                                    </div>
                                                    <div class="col-sm-1">
                                                        <?php // $form->field($modelVentaContiene, "[{$i}]valor_venta_unitario")->textInput(['hidden' => true])  ?>
                                                        <h4 class="text-info"><?= Html::encode('V/U:')?><br><?= Html::encode('$ ')?><span id = "<?= "ventacontiene-{$i}-valor_venta_unitario" ?>" class="text-default text"></span></h4>
                                                    </div>
                                                    <div class="col-sm-2">
                                                        <?= $form->field($modelVentaContiene, "[{$i}]cantidad")->textInput(['type' => 'number', 'class' => 'cantidad form-control']) ?>
                                                    </div>
                                                    <div class="col-sm-2">
                                                        <h4 class="text-info"><?= Html::encode('Subtotal:')?><br><?= Html::encode('$ ')?><span id = "<?= "ventacontiene-{$i}-subtotal" ?>" class="text-default"></span></h4>
                                                        <?php  // echo $form->field($modelRecetaContiene, "[{$i}]observaciones")->textInput(['maxlength' => true]) ?>
                                                    </div>
                                                </div><!-- row -->
                                            </div>
                                        </div>
                                    <?php endforeach; ?>
                                    </div>
                                    <?php DynamicFormWidget::end(); ?>
                                </div>
                        </div><!-- row -->
                        <div class="form-group">
                            <p id="es_nuevo" hidden="true"><?= $model->isNewRecord?'nuevo':'viejo'?></p>
                            <?php $form->field($modelVentaContiene, "[{$i}]valor_venta_unitario")->textInput(['hidden' => true])  ?>
                            <?php
                                if($bloquearEstado)
                                    echo Html::submitButton($model->isNewRecord ? Yii::t('app', 'Guardar') : Yii::t('app', 'Terminar'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-warning', 'id' => 'end']);
                                else
                                    echo Html::submitButton($model->isNewRecord ? Yii::t('app', 'Guardar') : Yii::t('app', 'Actualizar'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary', 'id' => 'save']); 
                                ?>
                        </div>
        </div><!--panel-body-->
    </div><!-- panel default -->
    <?php ActiveForm::end(); ?>
</div>

The update function in the controller:

public function actionUpdate($id, $close = false)
    {
        $model = $this->findModel($id);
        $modelsVentaContiene = $model->ventaContienes;

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            $oldIDs = ArrayHelper::map($modelsVentaContiene, 'id', 'id');
            $modelsVentaContiene = Model::createMultiple(VentaContiene::classname(), $modelsVentaContiene);
//--------------- here are the call that fail -----------------------
            Model::loadMultiple($modelsVentaContiene, Yii::$app->request->post());
//----------------------------------------------------------------------
            $deletedIDs = array_diff($oldIDs, array_filter(ArrayHelper::map($modelsVentaContiene, 'id', 'id')));

            // validate all models
            $valid = $model->validate();
            $valid = Model::validateMultiple($modelsVentaContiene) && $valid;

            if ($valid) {
                $costoTotal = 0;
                $valorVentaTotal = 0;
                $esCuentaCorriente = false;
                $cuentaCorrienteEncontrada = false;
                $transaction = \Yii::$app->db->beginTransaction();
                try {
                    $todoOk = true;
                    if ($flag = $model->save(false)) {
                        if (! empty($deletedIDs)) {
                            VentaContiene::deleteAll(['id' => $deletedIDs]);
                        }
                        foreach ($modelsVentaContiene as $modelVentaContiene) {
                        /*******************************************/
                          // a lot of stuff that doen't care
                        /*******************************************/
                        }
                        if ($todoOk) {
                            $model->save();
                        }
                    }
                    if ($flag) {
                        $transaction->commit();
                        return $this->redirect(['index', 'id' => $model->id]);
                    }
                } catch (Exception $e) {
                    $transaction->rollBack();
                }
            }
        } else {
            if($close){
                $model->estado_venta_id = 2;
                return $this->renderAjax('_form', [
                    'model' => $model,
                    'modelsVentaContiene' => (empty($modelsVentaContiene)) ? [new VentaContiene] : $modelsVentaContiene,
                    'bloquearEstado' => true,
                ]);
            } else {
                return $this->renderAjax('_form', [
                    'model' => $model,
                    'modelsVentaContiene' => (empty($modelsVentaContiene)) ? [new VentaContiene] : $modelsVentaContiene,
                    'bloquearEstado' => false,
                ]);
            }
        }
    }

And the loadMultiple function in base/Model.php

public static function loadMultiple($models, $data, $formName = null)
    {
        if ($formName === null) {
            /* @var $first Model */
            $first = reset($models);
            if ($first === false) {
                return false;
            }
            $formName = $first->formName();
        }

        $success = false;
        foreach ($models as $i => $model) {
            /* @var $model Model */
            if ($formName == '') {
                if (!empty($data[$i])) {
                    $model->load($data[$i], '');
                    $success = true;
                }
            } elseif (!empty($data[$formName][$i])) {
                $model->load($data[$formName][$i], '');
                $success = true;
            }
     }        
}
vikas225 commented 7 years ago

i'm also facing the same problem???anybody has its solution.

vikas225 commented 7 years ago

Hello friends i have found a solution for this i have added only one line in the model.php(vendor\yiisoft\yii2\base\model.php) in this function public static function loadMultiple($models, $data, $formName = null){ if(isset($data['MeetingDescription'])){ //check if array is not empty $data['MeetingDescription']=array_merge($data['MeetingDescription']);//it should put on just after the starting the function }

MeetingDescription is my subarray or sub table(which are dynamic)

Note:- This is not perfect solution but your work will not stop whenever your yii directory will update then your code will flush after update plz update this file also.