2016-07-07 8 views
2

In meinem Projekt habe ich eine Aggregation zwischen, sagen wir, ein Universitätsmodell und ein Abteilungsmodell: eine Universität hat mindestens eine Abteilung, während jede Abteilung nur einer Universität gehört. Ich hätte gerne die Möglichkeit, eine Universitätsmodellinstanz mit einer Anzahl von Abteilungsmodellinstanzen zu erstellen und die genaue Anzahl der Abteilungen ist nicht im Voraus bekannt (aber mindestens eine muss existieren). Also, wenn ich eine Universität erstelle, hätte ich gerne eine Seite mit einer Standardabteilung und einer Schaltfläche "Abteilung hinzufügen", die mir mittels Javascript erlauben würde, beliebig viele Abteilungen hinzuzufügen, die ich brauche.aktive Form multi Modell und multi Attribute in Yii2

Die Frage ist: wie soll ich das schaffen Ansicht Seite mit Activeform, um schreiben, dass mein POST-Array hat die folgende Struktur:

"University" => ["name" => "Sorbonne", "city" => "Paris"], 
    "Faculty" => [ 
     0 => ["name" => "Medicine", "dean" => "Person A"], 
     1 => ["name" => "Physics", "dean" => "Person B"], 
     2 => ["name" => "Mathematics", "dean" => "Person C"], 
     ... 
    ] 

, die ich Fakultät :: loadMultiple passiert dann() -Methode .

Ich habe versucht, so etwas wie diese

$form = ActiveForm::begin(); 
    echo $form->field($university, 'name')->textInput(); 
    echo $form->field($university, 'city')->textInput(); 
    foreach ($faculties as $i => $faculty) { 
     echo $form->field($faculty, "[$i]name")->textInput(); 
     echo $form->field($faculty, "[$i]dean")->textInput() 
    } 
    ActiveForm::end(); 

Es funktioniert, aber wenn mittels Javascript neue Abteilung hinzugefügt (I klont nur einen HTML-Knoten, die Abteilung Eingabefelder enthält), ich bin die auszuarbeiten gezwungen Zahlen, die von der Variablen $ i des obigen PHP-Skripts kommen. Und das ist ziemlich nervig.

Eine andere Möglichkeit, die ich habe versucht, von Variable $ i loszuwerden und schreiben etwas wie

$form = ActiveForm::begin(); 
    echo $form->field($university, 'name')->textInput(); 
    echo $form->field($university, 'city')->textInput(); 
    foreach ($faculties as $faculty) { 
     echo $form->field($faculty, "[]name")->textInput(); 
     echo $form->field($faculty, "[]dean")->textInput() 
    } 
    ActiveForm::end(); 

Auf diese Weise wird der entsprechende Knoten Klonen ist sehr einfach, aber die erzeugte POST-Array falsche Struktur aufgrund von [] Klammern.

Ist es möglich, den letzteren Ansatz zu ändern und die erforderliche Struktur des POST-Arrays zu haben?

Antwort

2

Verwenden Yii2 dynamische Form Erweiterung:

Installations

Der bevorzugte Weg, um diese Erweiterung zu installieren, ist durch Komponisten.

Entweder laufen:

composer require --prefer-dist wbraganca/yii2-dynamicform "dev-master" 

Oder der require Abschnitt Ihrer composer.json Datei hinzufügen:

"wbraganca/yii2-dynamicform": "dev-master" 

Demo Seite: Nested Dynamic Form

Nested dynamische Form Demo Source Code:

EER Diagram

Source Code - Ausblick: _form.php

<?php 

use yii\helpers\Html; 
use yii\bootstrap\ActiveForm; 
use wbraganca\dynamicform\DynamicFormWidget; 

?> 

<div class="person-form"> 

<?php $form = ActiveForm::begin(['id' => 'dynamic-  form']); ?> 

<div class="row"> 
    <div class="col-sm-6"> 
     <?= $form->field($modelPerson, 'first_name')->textInput(['maxlength' => true]) ?> 
    </div> 
    <div class="col-sm-6"> 
     <?= $form->field($modelPerson, 'last_name')->textInput(['maxlength' => true]) ?> 
    </div> 
</div> 

<div class="padding-v-md"> 
    <div class="line line-dashed"></div> 
</div> 

<?php DynamicFormWidget::begin([ 
    'widgetContainer' => 'dynamicform_wrapper', 
    'widgetBody' => '.container-items', 
    'widgetItem' => '.house-item', 
    'limit' => 10, 
    'min' => 1, 
    'insertButton' => '.add-house', 
    'deleteButton' => '.remove-house', 
    'model' => $modelsHouse[0], 
    'formId' => 'dynamic-form', 
    'formFields' => [ 
     'description', 
    ], 
]); ?> 
<table class="table table-bordered table-striped"> 
    <thead> 
     <tr> 
      <th>Houses</th> 
      <th style="width: 450px;">Rooms</th> 
      <th class="text-center" style="width: 90px;"> 
       <button type="button" class="add-house btn btn-success btn-xs"><span class="fa fa-plus"></span></button> 
      </th> 
     </tr> 
    </thead> 
    <tbody class="container-items"> 
    <?php foreach ($modelsHouse as $indexHouse => $modelHouse): ?> 
     <tr class="house-item"> 
      <td class="vcenter"> 
       <?php 
        // necessary for update action. 
        if (! $modelHouse->isNewRecord) { 
         echo Html::activeHiddenInput($modelHouse, "[{$indexHouse}]id"); 
        } 
       ?> 
       <?= $form->field($modelHouse, "[{$indexHouse}]description")->label(false)->textInput(['maxlength' => true]) ?> 
      </td> 
      <td> 
       <?= $this->render('_form-rooms', [ 
        'form' => $form, 
        'indexHouse' => $indexHouse, 
        'modelsRoom' => $modelsRoom[$indexHouse], 
       ]) ?> 
      </td> 
      <td class="text-center vcenter" style="width: 90px; verti"> 
       <button type="button" class="remove-house btn btn-danger btn-xs"><span class="fa fa-minus"></span></button> 
      </td> 
     </tr> 
    <?php endforeach; ?> 
    </tbody> 
</table> 
<?php DynamicFormWidget::end(); ?> 

<div class="form-group"> 
    <?= Html::submitButton($modelPerson->isNewRecord ? 'Create' : 'Update', ['class' => 'btn btn-primary']) ?> 
</div> 

<?php ActiveForm::end(); ?> 

Source Code - Ausblick: _form-Zimmer.

php
<?php 

use yii\helpers\Html; 
use wbraganca\dynamicform\DynamicFormWidget; 

?> 

<?php DynamicFormWidget::begin([ 
'widgetContainer' => 'dynamicform_inner', 
'widgetBody' => '.container-rooms', 
'widgetItem' => '.room-item', 
'limit' => 4, 
'min' => 1, 
'insertButton' => '.add-room', 
'deleteButton' => '.remove-room', 
'model' => $modelsRoom[0], 
'formId' => 'dynamic-form', 
'formFields' => [ 
    'description' 
], 
]); ?> 
<table class="table table-bordered"> 
<thead> 
    <tr> 
     <th>Description</th> 
     <th class="text-center"> 
      <button type="button" class="add-room btn btn-success btn-xs"><span class="glyphicon glyphicon-plus"></span></button> 
     </th> 
    </tr> 
</thead> 
<tbody class="container-rooms"> 
<?php foreach ($modelsRoom as $indexRoom => $modelRoom): ?> 
    <tr class="room-item"> 
     <td class="vcenter"> 
      <?php 
       // necessary for update action. 
       if (! $modelRoom->isNewRecord) { 
        echo Html::activeHiddenInput($modelRoom, "[{$indexHouse}][{$indexRoom}]id"); 
       } 
      ?> 
      <?= $form->field($modelRoom, "[{$indexHouse}][{$indexRoom}]description")->label(false)->textInput(['maxlength' => true]) ?> 
     </td> 
     <td class="text-center vcenter" style="width: 90px;"> 
      <button type="button" class="remove-room btn btn-danger btn-xs"><span class="glyphicon glyphicon-minus"></span></button> 
     </td> 
    </tr> 
<?php endforeach; ?> 
</tbody> 

Source Code - Regler

<?php 

namespace app\modules\yii2extensions\controllers; 

use Yii; 
use yii\helpers\ArrayHelper; 
use yii\web\NotFoundHttpException; 
use yii\web\Response; 
use yii\widgets\ActiveForm; 
use app\base\Model; 
use app\base\Controller; 
use app\modules\yii2extensions\models\House; 
use app\modules\yii2extensions\models\Person; 
use app\modules\yii2extensions\models\Room; 
use app\modules\yii2extensions\models\query\PersonQuery; 

/** 
* DynamicformDemo3Controller implements the CRUD actions for Person model. 
*/ 
class DynamicformDemo3Controller extends Controller 
{ 
/** 
* Lists all Person models. 
* @return mixed 
*/ 
public function actionIndex() 
{ 
    $searchModel = new PersonQuery(); 
    $dataProvider = $searchModel->search(Yii::$app->request->queryParams); 

    return $this->render('index', [ 
     'searchModel' => $searchModel, 
     'dataProvider' => $dataProvider, 
    ]); 
} 

/** 
* Displays a single Person model. 
* @param integer $id 
* @return mixed 
*/ 
public function actionView($id) 
{ 
    $model = $this->findModel($id); 
    $houses = $model->houses; 

    return $this->render('view', [ 
     'model' => $model, 
     'houses' => $houses, 
    ]); 
} 

/** 
* Creates a new Person model. 
* If creation is successful, the browser will be redirected to the 'view' page. 
* @return mixed 
*/ 
public function actionCreate() 
{ 
    $modelPerson = new Person; 
    $modelsHouse = [new House]; 
    $modelsRoom = [[new Room]]; 

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

     $modelsHouse = Model::createMultiple(House::classname()); 
     Model::loadMultiple($modelsHouse, Yii::$app->request->post()); 

     // validate person and houses models 
     $valid = $modelPerson->validate(); 
     $valid = Model::validateMultiple($modelsHouse) && $valid; 

     if (isset($_POST['Room'][0][0])) { 
      foreach ($_POST['Room'] as $indexHouse => $rooms) { 
       foreach ($rooms as $indexRoom => $room) { 
        $data['Room'] = $room; 
        $modelRoom = new Room; 
        $modelRoom->load($data); 
        $modelsRoom[$indexHouse][$indexRoom] = $modelRoom; 
        $valid = $modelRoom->validate(); 
       } 
      } 
     } 

     if ($valid) { 
      $transaction = Yii::$app->db->beginTransaction(); 
      try { 
       if ($flag = $modelPerson->save(false)) { 
        foreach ($modelsHouse as $indexHouse => $modelHouse) { 

         if ($flag === false) { 
          break; 
         } 

         $modelHouse->person_id = $modelPerson->id; 

         if (!($flag = $modelHouse->save(false))) { 
          break; 
         } 

         if (isset($modelsRoom[$indexHouse]) && is_array($modelsRoom[$indexHouse])) { 
          foreach ($modelsRoom[$indexHouse] as $indexRoom => $modelRoom) { 
           $modelRoom->house_id = $modelHouse->id; 
           if (!($flag = $modelRoom->save(false))) { 
            break; 
           } 
          } 
         } 
        } 
       } 

       if ($flag) { 
        $transaction->commit(); 
        return $this->redirect(['view', 'id' => $modelPerson->id]); 
       } else { 
        $transaction->rollBack(); 
       } 
      } catch (Exception $e) { 
       $transaction->rollBack(); 
      } 
     } 
    } 

    return $this->render('create', [ 
     'modelPerson' => $modelPerson, 
     'modelsHouse' => (empty($modelsHouse)) ? [new House] : $modelsHouse, 
     'modelsRoom' => (empty($modelsRoom)) ? [[new Room]] : $modelsRoom, 
    ]); 
} 

/** 
* Updates an existing Person model. 
* If update is successful, the browser will be redirected to the 'view' page. 
* @param integer $id 
* @return mixed 
*/ 
public function actionUpdate($id) 
{ 
    $modelPerson = $this->findModel($id); 
    $modelsHouse = $modelPerson->houses; 
    $modelsRoom = []; 
    $oldRooms = []; 

    if (!empty($modelsHouse)) { 
     foreach ($modelsHouse as $indexHouse => $modelHouse) { 
      $rooms = $modelHouse->rooms; 
      $modelsRoom[$indexHouse] = $rooms; 
      $oldRooms = ArrayHelper::merge(ArrayHelper::index($rooms, 'id'), $oldRooms); 
     } 
    } 

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

     // reset 
     $modelsRoom = []; 

     $oldHouseIDs = ArrayHelper::map($modelsHouse, 'id', 'id'); 
     $modelsHouse = Model::createMultiple(House::classname(), $modelsHouse); 
     Model::loadMultiple($modelsHouse, Yii::$app->request->post()); 
     $deletedHouseIDs = array_diff($oldHouseIDs, array_filter(ArrayHelper::map($modelsHouse, 'id', 'id'))); 

     // validate person and houses models 
     $valid = $modelPerson->validate(); 
     $valid = Model::validateMultiple($modelsHouse) && $valid; 

     $roomsIDs = []; 
     if (isset($_POST['Room'][0][0])) { 
      foreach ($_POST['Room'] as $indexHouse => $rooms) { 
       $roomsIDs = ArrayHelper::merge($roomsIDs, array_filter(ArrayHelper::getColumn($rooms, 'id'))); 
       foreach ($rooms as $indexRoom => $room) { 
        $data['Room'] = $room; 
        $modelRoom = (isset($room['id']) && isset($oldRooms[$room['id']])) ? $oldRooms[$room['id']] : new Room; 
        $modelRoom->load($data); 
        $modelsRoom[$indexHouse][$indexRoom] = $modelRoom; 
        $valid = $modelRoom->validate(); 
       } 
      } 
     } 

     $oldRoomsIDs = ArrayHelper::getColumn($oldRooms, 'id'); 
     $deletedRoomsIDs = array_diff($oldRoomsIDs, $roomsIDs); 

     if ($valid) { 
      $transaction = Yii::$app->db->beginTransaction(); 
      try { 
       if ($flag = $modelPerson->save(false)) { 

        if (! empty($deletedRoomsIDs)) { 
         Room::deleteAll(['id' => $deletedRoomsIDs]); 
        } 

        if (! empty($deletedHouseIDs)) { 
         House::deleteAll(['id' => $deletedHouseIDs]); 
        } 

        foreach ($modelsHouse as $indexHouse => $modelHouse) { 

         if ($flag === false) { 
          break; 
         } 

         $modelHouse->person_id = $modelPerson->id; 

         if (!($flag = $modelHouse->save(false))) { 
          break; 
         } 

         if (isset($modelsRoom[$indexHouse]) && is_array($modelsRoom[$indexHouse])) { 
          foreach ($modelsRoom[$indexHouse] as $indexRoom => $modelRoom) { 
           $modelRoom->house_id = $modelHouse->id; 
           if (!($flag = $modelRoom->save(false))) { 
            break; 
           } 
          } 
         } 
        } 
       } 

       if ($flag) { 
        $transaction->commit(); 
        return $this->redirect(['view', 'id' => $modelPerson->id]); 
       } else { 
        $transaction->rollBack(); 
       } 
      } catch (Exception $e) { 
       $transaction->rollBack(); 
      } 
     } 
    } 

    return $this->render('update', [ 
     'modelPerson' => $modelPerson, 
     'modelsHouse' => (empty($modelsHouse)) ? [new House] : $modelsHouse, 
     'modelsRoom' => (empty($modelsRoom)) ? [[new Room]] : $modelsRoom 
    ]); 
} 

/** 
* Deletes an existing Person model. 
* If deletion is successful, the browser will be redirected to the 'index' page. 
* @param integer $id 
* @return mixed 
*/ 
public function actionDelete($id) 
{ 
    $model = $this->findModel($id); 
    $name = $model->first_name; 

    if ($model->delete()) { 
     Yii::$app->session->setFlash('success', 'Record <strong>"' . $name . '"</strong> deleted successfully.'); 
    } 

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

/** 
* Finds the Person model based on its primary key value. 
* If the model is not found, a 404 HTTP exception will be thrown. 
* @param integer $id 
* @return Person the loaded model 
* @throws NotFoundHttpException if the model cannot be found 
*/ 
protected function findModel($id) 
{ 
    if (($model = Person::findOne($id)) !== null) { 
     return $model; 
    } else { 
     throw new NotFoundHttpException('The requested page does not exist.'); 
    } 
} 
} 
+0

Danke, es ist genau das, was ich brauchte. – Andrew