Yii 动态模型实现思路

发布时间 2023-11-02 22:39:04作者: 两双筷子

假设这么一个场景: 有一个通过 Yii ActiveForm 构建的表单,里面所有的字段都是虚拟的,比如说 这个表单的字段来自于一个 JSON 或者说数据库的结果集,需要实现同一个模型,根据不同的传参,构建不同的表单项。

问题: Yii 的表单是通过模型来创建的,也就是说我有这个表单项,模型中必须有对应的字段(成员属性),否则就会报错。且这个成员属性在模型实例化之后是不允许添加的,那么我们如何去动态的添加这些表单项的字段?

解决思路: 既然模型字段都是不存在的,我们干脆用一个虚拟的模型,不是说这个模型文件不存在,而是指模型文件不对应任何实际的数据表。
那么我们只需要在实例化模型的时候,传入动态的字段,然后在对应的 rules()attributes()attributeLabels() 方法里面返回我们的动态字段,就可以实现需要的效果。

模型部分

创建一个虚拟的模型,重写构造方法,传入字段,赋值给成员属性 $field

<?php
namespace app\models;

use Attribute;
use yii\db\ActiveRecord;
class Actor extends ActiveRecord
{
    public $field;
    public function __construct($field)
    {
        $this->field = $field;
        parent::__construct([]);
    }


    public function rules()
    {
        return [
            [$this->field, 'required']
        ];
    }

    public function attributes()
    {
        return $this->field;
    }
}

控制器部分

<?php

namespace app\controllers;

use Yii;
use Yii\base\Controller;
use app\models\Actor;

class TestController extends Controller
{

    public function actionIndex()
    {
        /**
         * 动态创建的字段,在实例化模型的时候传递进去
         * 这里只是为了测试,实际应用的时候可以是从其他途径来的数组:
         *  比如 JSON 动态转换、数据库查询出的结果集等等
         */
        $field = ['username','json','token'];
        $model = new Actor($field);

        if (Yii::$app->request->getIsGet()) {
            return $this->render('index',['model' => $model]);
        }

        // 装载数据
        $model->load(Yii::$app->request->post());
        // 获取模型提交的数据
        var_dump($model->getDirtyAttributes());
    }

}

视图部分

<?php
use yii\helpers\Html;
use yii\widgets\ActiveForm;

$form = ActiveForm::begin([
    'id' => 'login-form',
    'options' => ['class' => 'form-horizontal'],
]) ?>
    <!-- 遍历模型中的字段 在页面上构建输入框 -->
    <? foreach($model->field as $field) { ?>
        <?= $form->field($model, $field) ?>
    <? }?>
    <div class="form-group">
        <div class="col-lg-offset-1 col-lg-11">
            <?= Html::submitButton('submit', ['class' => 'btn btn-primary']) ?>
        </div>
    </div>
<?php ActiveForm::end() ?>