动态创建 Yii FormModel 对象(CFormModel)

2022-01-04 00:00:00 class forms magic-methods php yii

我正在开发一个涉及在高级抽象级别生成表单的应用程序(它是一个 CMS 应用程序).我想动态创建 CFormModel 对象并即时设置表单字段.我想我可以通过扩展 CFormModel 来做到这一点,然后动态创建表示表单字段的类属性(Yii 术语中的属性").

I'm working on an application that involves generating forms at a high level of abstraction (it's a CMS app). I want to dynamically create CFormModel objects and set the form fields on-the-fly. I think I can do this by extending CFormModel, and then dynamically creating the class properites that represent the form fields ('attributes' in the Yii lingo).

为了说明,而不是在以下类中指定登录表单(在文件中定义):

To illustrate, instead of specifying a login form in the following class (defined in a file):

// From: http://www.yiiframework.com/doc/guide/1.1/en/form.model
class LoginForm extends CFormModel
{
    public $username;
    public $password;
    public $rememberMe=false;

    private $_identity;

    public function rules()
    {
        return array(
            array('username, password', 'required'),
            array('rememberMe', 'boolean'),
            array('password', 'authenticate'), // assume function authenticate defined elsewhere
        );
    }
}

我想这样做:

class MyFormModel extends CFormModel {

    protected $_rules = array();

    public function __construct($attributes=array(), $rules=array()) {

        foreach ($attributes as $i => $attr) {
            $this->{$attr} = ???; // <<== This is the key here
        }

        // pass in array of rules as described in Yii doc for CFormModel
        $this->_rules = $rules;
    }

    public function rules() {
        return $_rules;
    }
}

并在需要时调用它,如下所示:

And invoke it when needed as follows:

$myModelObj = new MyFormModel($attr, $rules);

哪里:

$attr = array(
            'username',
            'rememberMe',
            'password',
        );
$rules = array(
            array('username, password', 'required'),
            array('rememberMe', 'boolean'),
            array('password', 'authenticate'), // assume function authenticate defined elsewhere
        );

请注意,在我尝试完成的过程中,没有在任何文件中写入LoginClass",它是在代码中即时创建的.

Note in what I'm trying to accomplish, there is no "LoginClass" written in any file, it's created on-the-fly in code.

这将允许我创建表单(在视图中)做这样的事情:

This would allo me to create forms (in views) doing stuff like this:

// based on http://www.yiiframework.com/doc/guide/1.1/en/form.view
<?php echo $wForm->textField($myModelObj,'username'); ?>

我试过了,$this->{$attr} 行失败了:

I've tried this, and the $this->{$attr} line is failing with:

Property "MyFormModel.username" is not defined.

实际上该行的代码只是:

Actually the code for that line is just:

$this->{$attr};

???表示我不确定要分配给它什么.在 Yii 文档示例中,他们只是将字段定义为公共类变量.

The ??? indicates I'm not really sure what to assign to this. In the Yii doc examples, they just define the fields as public class variables.

我应该使用魔法方法吗?

Should I be using magic methods?

我在这里尝试做的事情有可能吗?

Is what I'm trying to do here even possible?

推荐答案

你可能知道 Yii 使用 OOP 重载 来解决您的类似 AR 的属性.

As you probably know Yii uses OOP overloading to resolve your AR like properties.

这里需要做的和 Yii 内部做的类似.

What you need to do here is similar to what Yii internally does.

定义一个硬编码属性来像数组一样存储所有自定义属性:$_data

Define a hardcoded property to store all your custom properties like an array: $_data

并且此数据将是一个数组,并将保存您在运行时添加的所有属性.您可能需要像 Yii 一样通过覆盖魔法方法(setter、getter、isset)来挑战验证,首先从您的 $_data 属性持有者中解析属性名称.

and this data will be an array and will hold all your attributes that you added at runtime. You may need to challenge the validation by overwriting the magic methods (setters,getters,isset) as Yii does, to first resolve the property names FROM your $_data property holder.

您会在 CActiveRecord 寻找所有那些 __XXX 之类的方法.

Some sort of code you will find in CActiveRecord look for all those __XXX like methods.

如果您将 AR 属性处理复制到您的自定义类中,您将在您的级别运行所有这些,并且当您的魔术方法无法解析它时将回退到 Yii.

If you duplicate the AR property handling to your custom class, you will get all this running at your level, and will fall back to Yii when your magic methods are not resolving it.

此外,我会研究行为,因为您可以将许多常用功能委托给行为类.

Also I would look into Behaviours as you can delegate a lot of common function to a behaviour class.

组件行为的使用

组件支持 mixin 模式,并且可以附加一个或多个行为.行为是一个对象,其方法可以通过其附加组件通过收集功能而不是特化(即正常的类继承)的方式继承".一个组件可以附加多个行为,从而实现多重继承".

A component supports the mixin pattern and can be attached with one or several behaviors. A behavior is an object whose methods can be 'inherited' by its attached component through the means of collecting functionality instead of specialization (i.e., normal class inheritance). A component can be attached with several behaviors and thus achieve 'multiple inheritance'.

行为类必须实现IBehavior 接口.大多数行为都可以从 CBehavior 基类扩展.如果需要将行为附加到模型,它也可以从 CModelBehavior 扩展或 CActiveRecordBehavior,它实现了特定于模型的附加功能.

Behavior classes must implement the IBehavior interface. Most behaviors can extend from the CBehavior base class. If a behavior needs to be attached to a model, it may also extend from CModelBehavior or CActiveRecordBehavior which implements additional features specifc for models.

要使用行为,必须首先通过调用行为的attach() 方法将其附加到组件.然后我们可以通过组件调用一个行为方法:

To use a behavior, it must be attached to a component first by calling the behavior's attach() method. Then we can call a behavior method via the component:

// $name uniquely identifies the behavior in the component
$component->attachBehavior($name,$behavior);
// test() is a method of $behavior
$component->test();

附加的行为可以像组件的普通属性一样访问.例如,如果一个名为 tree 的行为附加到一个组件,我们可以使用以下方法获取对这个行为对象的引用:

An attached behavior can be accessed like a normal property of the component. For example, if a behavior named tree is attached to a component, we can obtain the reference to this behavior object using:

$behavior=$component->tree;
// equivalent to the following:
// $behavior=$component->asa('tree');

可以暂时禁用行为,使其方法无法通过组件使用.例如,

A behavior can be temporarily disabled so that its methods are not available via the component. For example,

$component->disableBehavior($name);
// the following statement will throw an exception
$component->test();
$component->enableBehavior($name);
// it works now
$component->test();

附加到同一个组件的两个行为可能具有相同名称的方法.在这种情况下,第一个附加行为的方法将优先.

It is possible that two behaviors attached to the same component have methods of the same name. In this case, the method of the first attached behavior will take precedence.

当与事件一起使用时,行为更强大.一个行为,当被附加到一个组件时,可以将它的一些方法附加到该组件的一些事件上.通过这样做,行为有机会观察或改变组件的正常执行流程.

When used together with events, behaviors are even more powerful. A behavior, when being attached to a component, can attach some of its methods to some events of the component. By doing so, the behavior gets a chance to observe or change the normal execution flow of the component.

一个行为的属性也可以通过它所附加的组件来访问.属性包括公共成员变量和通过行为的 getter 和/或 setter 定义的属性.例如,如果一个行为有一个名为 xyz 的属性,并且该行为附加到一个组件 $a 上.然后我们可以使用表达式 $a->xyz 来访问行为的属性.

A behavior's properties can also be accessed via the component it is attached to. The properties include both the public member variables and the properties defined via getters and/or setters of the behavior. For example, if a behavior has a property named xyz and the behavior is attached to a component $a. Then we can use the expression $a->xyz to access the behavior's property.

更多阅读:
http://www.yiiframework.com/wiki/44/behaviors-events
http://www.ramirezcobos.com/2010/11/19/how-to-create-a-yii-behavior/

相关文章