2

在我的项目中,我在大学模型和系模型之间进行了聚合:一所大学至少有一个系,而每个系只属于一所大学。我希望有可能创建一个具有一些部门模型实例的大学模型实例,并且事先不知道部门的确切数量(但至少必须存在一个)。因此,在创建一所大学时,我希望有一个页面,其中包含一个默认部门和一个“添加部门”按钮,允许我通过 javascript 添加我需要的任意数量的部门。

问题是:我应该如何使用 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"],
       ...
   ]

然后我将其传递给 Faculty::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 节点),我不得不详细说明来自上述 php 脚本的变量 $i 的数字。这很烦人。

我尝试过的另一种可能性是摆脱变量 $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 数组结构?

4

1 回答 1

2

使用 Yii2 动态表单扩展:

安装

安装此扩展的首选方法是通过 composer。

要么运行:

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

或者添加到 composer.json 文件的 require 部分:

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

演示页面:嵌套动态表单

嵌套动态表单演示源代码:

能效比图

源代码 - 查看:_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-rooms.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.');
    }
}
}
于 2016-11-03T11:24:23.243 回答