2016-07-07 4 views
2

В моем проекте у меня есть совокупность между, скажем, университетской моделью и моделью факультета: в университете есть как минимум один отдел, в то время как каждый отдел принадлежит только одному университету. Я хотел бы иметь возможность создать экземпляр модели университета с некоторым количеством экземпляров модели отдела, и точное количество отделов неизвестно заранее (но, по крайней мере, один должен существовать). Поэтому, создавая университет, я хотел бы иметь страницу с одним департаментом по умолчанию и кнопку «Добавить отдел», которая позволит мне с помощью javascript добавлять любое количество отделов, которые мне нужны.активная форма мульти модели и несколько атрибутов в Yii2

Вопрос заключается в том: как я должен написать страницу создать представление с помощью ActiveForm для того, чтобы мой массив POST имеет следующую структуру: метод

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

, что я потом перейти на факультет :: loadMultiple() ,

Я пытался что-то вроде этого

$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(); 

Он работает, но при добавлении нового отдела с помощью JavaScript (я просто клонировать HTML узел, содержащий входные поля отдела), я вынужден разработать числа, поступающие из переменной $ i указанного выше скрипта php. И это довольно раздражает.

Другой возможность, что я попытался было избавиться от переменных $ I и написать что-то вроде

$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(); 

Таким образом, клонирования соответствующего узла является очень простым, но сгенерированный массив POST имеет неправильную структуру из-за [] скобок.

Возможно ли изменить последний подход и иметь требуемую структуру массива POST?

ответ

2

Использование Yii2 динамической формы расширения:

Установка

Предпочтительный способ установить это расширение через композитора.

Либо запустить:

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

Или добавить в раздел требует вашего файла composer.json:

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

Демо страницы: Nested Dynamic Form

вложенную Динамическая форма демонстрационном Исходный код:

EER Diagram

Исходный код - Вид: _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(); ?> 

Исходный код - Вид: _form комнаты.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> 

Исходный код - Контроллер

<?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

Спасибо, это именно то, что мне было нужно. – Andrew