Como manipular los datos de un multiselect con Kartik Select2

Hola a todos
Pues necesito saber como puedo obtener en una lista o array todos los elementos que selecciono con el widget Select2 de kartik con la propiedad multiselect en true.

La situacion es la siguiente:
Tengo un formulario que almacena las datos introducidos en una tabla, que tiene un widget select2 de kartik, en el que el usuario debe seleccionar uno o varios articulos de la lista del select2, para despues almacenarlos en otra tabla de la BD.

Inicialmente el Select2 era simple, y el valor seleccionado se almacenaba en la misma tabla, pero ahora el usuario plantea que se puede seleccionar mas de un articulo (lo que seria una relacion uno a muchos), y quisiera sin modificar mucho la estructura del formulario resolver la situacion.
Por eso ahora el kartik Select2 lo modifico a multiselect, y se pueden seleccionar varios articulos sin problemas, peeero no se que hacer con esa seleccion, ya que por supuesto no se puede almacenar en el mismo campo de la BD (que es un ID de la relacion del lado “uno”)
Y la idea es guardar esa lista en el lado “muchos” de la tabla relacionada

Como puedo resolver esta situación?

/* Moved to /International/Spanish */

Ya lo solucione, el campo asociado al Select2 me devuelve un array con todos los item seleccionados
Despues recorro el array y lo guardo en la tabla

Hola @JoAnCa en que parte recorres el arreglo que te devuelve el multiselect? en el formulario o en el controlador?

Lo hago en el controlador, antes de guardar el registro.
Es decir, obtengo el array del select2 y lo guardo en la tabla correspondiente, le asigno null al campo asociado al select2 y después guardo el registro.

Quizás no sea el método mas optimo, pero me funcionó así.

1 Like

me podrias mostras algun ejemplo de como obtenes el array en el controller? @JoAnCa

Lo que hago es asignar el valor que me retorna el select2 a una variable, y le asigno null al campo asociado al select, ya que es campo entero en la BD y solo admite un valor.
Despues recorro el array de la variable y guardo esos valores en otra tabla.

        if ($model->load(Yii::$app->request->post())) {
            $consultores = $model->IDTRABAJADOR;  
            $model->IDTRABAJADOR = null;

            if ($model->save()) {
                if (isset($consultores)) {
                    $cant = count($consultores);
                    $importe = $model->Tarifa / $cant;   
                    foreach ($consultores as $value) {
                        $consult = new ContConsultores();
                            $consult->IDCONTRATO = $model->IdContrato;
                            $consult->IDTRABAJADOR = $value;
                            $consult->Importe = $importe;
                            $consult->IDAREARESP = $model->IDAREARESP;
                            $consult->FechaInicio = $model->FechaInicio;
                            $consult->FechaFin = $model->FechaFinal;
                        $consult->save();
                    }                
                }
                $model->refresh();
                Yii::$app->response->format = Response::FORMAT_JSON;

                return [
                    'message' => 'Contrato guardado correctamente',
                ];
            } else {
                Yii::$app->response->format = Response::FORMAT_JSON;
                return ActiveForm::validate($model);
            }
        }

Como dije antes, no se si hay otra forma mas eficiente, pero funciona así.

Creo que puedes quitar alguna línea por ahí, pero funciona, que es lo importante.

Lo que te puedo recomendar es alguna extensión para manejar esos campos 1:n que recoges en el select2, y que te ahorrarían hacer el loop cada vez que tengas uno de esos en un formulario.

Por ejemplo esta extensión GitHub - mootensai/yii2-relation-trait: Yii 2 Models add functionality for load with relation, & transactional save with relation PLUS soft delete/restore feature

Te permite declarar un array con todos los campos relacionados en un modelo. Luego en el controlador, usando loadAll() y saveAll(), ya no necesitas hacer un loop para salvar todos los campos relacionados cada vez que tengas un multiselect.

Por ejemplo, en tu caso:

En el modelo (lo he llamado Proyecto como ejemplo):

class Proyecto extends \yii\db\ActiveRecord
{
    use \mootensai\relation\RelationTrait;

     /**
     * Esto ayuda a \mootensai\relation\RelationTrait a ir más rápido
     * @return array nombre de la relación de este modelo
     */
    public function relationNames()
    {
        return [
          'consultores',
        ];
    }

También necesitarás definir la relación n el mismo modelo:

/**
     * @return ActiveQuery
     */
    public function getConsultores()
    {
        return $this->hasMany(\app\models\ContConsultores::class, ['proyecto_id' => 'id']);
    }

Y en el controlador puedes usar ahora saveAll, loadAll y deleteWithRelated para automatizar la cosa:

public function actionCreate()
    {
        $model = new Proyecto();

        if ($model->loadAll(Yii::$app->request->post()) && $model->saveAll()) {
            return $this->redirect(['view', 'id' => $model->id]);
        } else {
            return $this->render('create', [
                'model' => $model,
            ]);
        }
    }

O para borrar un registro:

public function actionDelete($id)
    {
        $this->findModel($id)->deleteWithRelated();

        return $this->redirect(['index']);
    }

Hay varias extensiones y traits por ahí, es sólo un ejemplo para ahorrarte tiempo.