PHPackages                             handcode/yii2-dynamic-form-model - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Utility &amp; Helpers](/categories/utility)
4. /
5. handcode/yii2-dynamic-form-model

ActiveYii2-extension[Utility &amp; Helpers](/categories/utility)

handcode/yii2-dynamic-form-model
================================

DynamicFormModel that is configurable with a given config struct

1.0.1(1mo ago)14BSD-3-ClausePHPPHP ^8.0

Since Jul 9Pushed 1mo agoCompare

[ Source](https://github.com/handcode/yii2-dynamic-form-model)[ Packagist](https://packagist.org/packages/handcode/yii2-dynamic-form-model)[ RSS](/packages/handcode-yii2-dynamic-form-model/feed)WikiDiscussions master Synced 1w ago

READMEChangelogDependencies (1)Versions (3)Used By (0)

yii2 DynamicFormModel
=====================

[](#yii2-dynamicformmodel)

`DynamicFormModel` extends yii2 `DynamicModel` and is highly configurable with a config struct and provide methods to get attributes, the activeForm fields and input validation defined in the struct.

As the input (the struct and the attribute values) and the output (attribute values after validation) can be JSON, this class can be used to define dynamic ContentTypes where the struct of the type and the content values can or should be stored as JSON string e.g. in a DB.

As `DynamicFormModel` use yii2 base model functionality, activeForm fields, yii2 validation rules etc. you can define (nearly) all yii2 model validation and activeForm definitions that are possible.

- default and custom validators, etc.
- default activeFormFields, InputWidgets etc.

### hint: attributes vs. properties

[](#hint-attributes-vs-properties)

The term `attribute(s)` is highly used inside yii models to access `properties` and therefor we use the term 'properties' in the struct to define `attributes` ;-)

### hint: closures

[](#hint-closures)

closures as option values are allowed in various places, but can only be used if the struct is plain PHP and NOT defined/used/safed as JSON, as we can not serialize closures!

If you want to use callbacks in serializable structs you must define the callbacks as callable functions or (static) methods in your code and then set the name as callback in the struct.

dynamic properties (attributes)
-------------------------------

[](#dynamic-properties-attributes)

Like in the yii2 base `DynamicModel` attributes and their values can be simply set via constructor.

In addition to the base `DynamicModel` class, the attributes (first constructor param) can also be provided as JSON string.

So this is all valid and equal:

```
$dynamicFormModelValues = ['pageId' => 5];
$dynamicFormModel = Yii::createObject(DynamicFormModel::class, [$dynamicFormModelValues]);
```

```
$dynamicFormModelValues = json_decode('{"pageId": 5}', JSON_OBJECT_AS_ARRAY);
$dynamicFormModel = Yii::createObject(DynamicFormModel::class, [$dynamicFormModelValues]);
```

```
$dynamicFormModelValues = '{"pageId": 5}';
$dynamicFormModel = Yii::createObject(DynamicFormModel::class, [$dynamicFormModelValues]);
```

The additional config struct can be applied by calling `DynamicFormModel::setStruct($struct)`

### nested properties

[](#nested-properties)

Beside the "simple" definition of scalar attributes like strings, integer etc., properties can be of `type` `object`which allows us to define structured property sets. Each `object` property creates its own, auto-generated `DynamicFormModel` instance.

As child objects are full-featured `DynamicFormModel` instances, they can also have properties of `type` `object`, which will result in further child objects, which also can have objects, which....

The default input and rules definition from the "parent" level of the given struct are inherited to the child objects, but can of course also be overridden at any level.

To validate objects recursively a validation rule with the property name of the child object is added to its parent. The Validator in the rule is set to the configured `DynamicFormModel::$objectPropertyValidationClass`. Default implementation of such a validator is [ObjectPropertyValidator](./src/ObjectPropertyValidator.php)

In order to preserve the nested structure of properties in HTML forms, the FormNames of the subobjects are automatically set accordingly.

To reflect the structure also in the HTML form while rendering the formFields, formFields of nested objects are encapsulated in (nested) fieldsets.

See the [`nested struct example`](#nested-struct-example) below.

config struct
-------------

[](#config-struct)

The struct is an array (or a JSON string that can be encoded to an array) with these keys:

- `formName` (string): defines the Model scope via @see Model::formName()
- `rules` (array|string): the (default)rules used for validating input values
- `properties` (array): define the names that should be "public properties" of the model. if empty or not set, the keys from `DynamicFormModel::getAttributes()` is used
- the `properties` can be:
    - string that simply define the property name
    - array where the key is the property name and the value is an array of configOptions for the property
    - if `type` is *'object'* a nested config struct definition is expected
- `fieldSet`: when defined the input for all properties that have NO self fieldSet defined will be rendered within a filedSet Tag with given name as set-legend. When not defined these inputs will not be rendered in any fieldSets
- property options can be:
    - `type`: can be 'attribute' or 'object', if 'object' the property will be instantiated as a `DynamicFormModel` itself, see [`nested struct example`](#nested-struct-example) below.
    - `label`: the label returned for this property, @see Model::getAttributeLabel()
    - `hint`: the hint returned for this property, @see Model::getAttributeHint()
    - `rules`: beside the defaultRules (see above) every property can define its own rules as subarray inside the properties
    - `input`: the config for the form input that will be rendered for the property when `DynamicFormModel::formFields()` is called
    - `fieldSet`: when defined the input for this property will be rendered within a filedSet Tag with given name as set-legend.

For the defined keys within the struct array explained above it is recommended to use the provided class constants. The constants are defined in the [DynamicFormModelInterface](./src/DynamicFormModelInterface.php)

For a more detailed definition of the struct schema see: [./docs/struct-schema](./docs/struct-schema.md)

or read the code ;-) [DynamicFormModel](./src/DynamicFormModel.php)

formFields for the model properties
-----------------------------------

[](#formfields-for-the-model-properties)

Beside the definition of properties and rules, the model provide a Method to get the ActiveFormFields for this model which can be displayed within an ActiveForm

This method will return form-fields for the given ActiveForm according to the dynamically assigned model properties and their input configs.

`self::formFields($form)`

Q: Why only form fields and not a full ActiveForm?

A: Out of scope.

The purpose of this class is to dynamically generate properties, their validation rules, and the corresponding form fields. However, it does not determine what should happen to the data—whether the form fields are “only” part of a more complex ActiveForm with other models, or perhaps just the more complex data structure of an attribute from a completely different model class, such as for a content model with fixed meta attributes but dynamic content.

### grouping formFields

[](#grouping-formfields)

- formFields can be grouped with the `fieldSet` option.
- properties without `fieldSet` option, are added without fieldset or - if defined - to the `fieldSet` defined at root level.
- to define nested groups of form elements, use nested object properties. see [`nested struct example`](#nested-struct-example) in the examples below.

getting the values
------------------

[](#getting-the-values)

### as Array

[](#as-array)

As `DynamicFormModel` is an `\yii\base\Model` which implements the `\yii\base\Arrayable`interface the values can be accessed as array with the `toArray()` Method.

`$model->toArray()`

### as JSON

[](#as-json)

To be able to persist the values of this model it implements the `JsonSerializable` interface and therefor can be stored as json simply be using

`json_encode($model)`

or with the wrapper method:

`$model->toJson()`

### rules config:

[](#rules-config)

Rules Config can be done

- on the top level of the struct, which will than be the default for all properties which does not provide own rules config.
- as subarray of a property config in the struct

#### Format:

[](#format)

- The given rules can be an array of rule arrays or a string which is simply treated as the validator name if no options are required for the rule validator
- The resulting array should be an array of valid validation rules WITHOUT the first param (the property name)
- The property name will be appended dynamically while initializing the property rules from the given struct

#### Examples:

[](#examples)

expected rule for e.g. property `name` should be:

```
$rules[] = [['name'], 'string', 'max' => 255]
```

the rules struct should be defined like:

```
['rules' => [
     ['string', 'max' => 255]
   ]
]
```

you can define multiple rules

expected rules for property `name` should be:

```
$rules[] = [['name'], 'trim'];
$rules[] = [['name'], 'string', 'max' => 255];
```

the rules struct should be defined like:

```
['rules' => [
   'trim',
   ['string', 'max' => 255]
]
```

if no "fancy" options are required, a rule can be defined as simple string:

```
['rules' => [
   'integer',
]
```

you can add filter rules with callbacks.

```
['rules' => [
    'integer',
    ['exist', 'skipOnError' => true, 'targetClass' => Page::class, 'targetAttribute' => ['pageId' => 'id']]
]
```

or

```
['rules' => [
      'trim',
      ['string', 'skipOnEmpty' => false, 'max' => 30],
      ['filter', 'filter' => 'strtolower', 'skipOnEmpty' => true],
      ['filter', 'filter' => [Inflector::class, 'slug'], 'skipOnEmpty' => true],
    ]
]
```

**Hint**closures in filter rules will only work, if the struct is plain PHP and NOT defined/safed as Json, as we can not serialize closures!

### input config

[](#input-config)

Input Config can be done

- on the top level of the struct, which will than be the default for all properties which does not provide their own input config.
- as subarray of an property config in the struct

The struct of the input config looks like:

```
['input' = [
    'inputCallback' => null,
    'widget' => null,
    'type' => null,
    'options' => [],
    'items' => null,
    'itemValues' => null,
    'itemsCallback' => null,
    ]
]
```

For a more detailed definition of the struct schema see: [./docs/struct-schema](./docs/struct-schema.md)

Examples
--------

[](#examples-1)

### php struct examples

[](#php-struct-examples)

```
# init the model with values from e.g. json string stored in the DB
$dynamicFormModelValues = !empty($this->params) ? \yii\helpers\Json::decode($this->params) : [];
$dynamicFormModel       = Yii::createObject(DynamicFormModel::class, [$dynamicFormModelValues]);

# define the struct for the model, its properties, rules and input types.
$dynamicFromConfig =
    [
        DynamicFormModel::KEY_NAME => 'dynamicPageParamsModel',
        DynamicFormModel::KEY_PROPERTIES => [
            'pageId' => [
                DynamicFormModel::KEY_RULES => [
                    'integer'
                ],
                DynamicFormModel::KEY_INPUT => [
                    DynamicFormModel::KEY_INPUT_TYPE => 'dropDownList',
                    // define callback which should provide the items array for the dropDownList
                    // ! be aware, that only callback config like this, which is serializable as
                    // ! json can be used IF the config should be read/stored as (json)string, e.g. in a DB
                    DynamicFormModel::KEY_INPUT_LIST_ITEMS_CALLBACK => [
                        Page::class,
                        'getOpts'
                    ],
                ],
            ],
            'password' => [
                // simply define input type = password
                DynamicFormModel::KEY_INPUT => 'password'
            ],
            'title' => [
                DynamicFormModel::KEY_LABEL => 'Title param',
                DynamicFormModel::KEY_HINT => 'String that will be used as value for the title attribute of the model'
            ],
            'intMask' => [
                DynamicFormModel::KEY_HINT => 'test for yii2 MaskedInput widget',
                DynamicFormModel::KEY_INPUT => [
                    // define the inputWidgetClass and it's options that should be initialized
                    // when the form field for this property is rendered.
                    DynamicFormModel::KEY_INPUT_WIDGET => MaskedInput::class,
                    DynamicFormModel::KEY_INPUT_OPTIONS => [
                        'mask' => '99-99',
                    ],
                ],
            ],
            'status' => [
                DynamicFormModel::KEY_INPUT => [
                    // define callback which should provide the formInput for this property
                    // ! be aware, that callback config like this, which is NOT serializable as
                    // ! json can NOT be used IF the config should be read/stored as (json)string
                    // ! e.g. in a DB
                    DynamicFormModel::KEY_INPUT_CALLBACK => function($form, $model, $property, $options) {
                        $items = [0 => YII::t('app', 'inactive'), 1 => YII::t('app', 'active')];
                        $model->$property ?: $model->$property = 0;
                        return $form->field($model, $property)->radioList($items, $options);
                    },
                ]
            ]
        ],
        DynamicFormModel::KEY_INPUT => ['text'],
        DynamicFormModel::KEY_RULES => [
            'trim',
            ['string', 'skipOnEmpty' => false, 'max' => 20]
        ]
    ];
$dynamicFormModel->setStruct($dynamicFromConfig);
?>
