Eduardo Eduardo - 1 year ago 267
PHP Question

Yii2. Adding attribute and rule dinamically to model

I am writing a widget and I want to avoid user adding code to their model (I know it would be easier but using it to learn something new).

Do you know if it is possible to add an attribute (which is not in your database, so it will be virtual) to a model and add a rule for that attribute?. You have no access to change that model code.

I know rules is an array. In the past I have merged rules from parent class using array_merge. Can it be done externally? Does Yii2 has a method for that?

An idea is to extend model provided by the user with a "model" inside my widget an there use:

public function init() {
/*Since it is extended this not even would be necessary,
I could declare the attribute as usual*/

$attribute = "categories";
$this->{$attribute} = null; //To create attribute on the fly


public function rules() {
$rules = [...];

//Then here merge parent rules with mine.
return array_merge(parent::rules, $rules);

But If I extend it, when I use that model in an ActiveForm in example for a checkbox, it will use my "CustomModel", so I want to avoid that. Any other ideas? How to do it without extending their model?

Answer Source

Add Dynamic Attributes to a Model

When you want to add dynamic attributes during runtime to a existing model. Then you need some custom code, you need: A Model-Class, and a extended class, which will do the dynamic part and which have array to hold the dynamic information. These array will merged in the needed function with the return arrays of the Model-Class.

Here is a kind of mockup, it's not fully working. But maybe you get an idea what you need to do:

class MyDynamicModel extends MyNoneDynamicModel

private $dynamicFields = [];
private $dynamicRules = [];

public function setDynamicFields($aryDynamics) {
     $this->dynamicFields = $aryDynamics;

public function setDynamicRules($aryDynamics) {
     $this->dynamicRules = $aryDynamics;

public function __get($name)
    if (isset($this->dynamicFields[$name])) {
        return $this->dynamicFields[$name];

    return parent::__get($name);

public function __set($name, $value)
if (isset($this->dynamicFields[$name])) {
  return $this->dynamicFields[$name] = $value;
return parent::__set($name, $value);

public function rules() {
  return array_merge(parent::rules, $this->dynamicRules);

Full Dynamic Attribute

When all attributes are dynamic and you don't need a database. Then use the DynamicModel of Yii2. The doc states also:

DynamicModel is a model class primarily used to support ad hoc data validation.

Here is a full example with form integration from the Yii2-Wiki, so i don't make a example here.

Virtual Attribute

When you want to add a attribute to the model, which is not in the database. Then just declare a public variable in the model:

public $myVirtualAttribute;

Then you can just use it in the rules like the other (database-)attributes.

To do Massive Assignment don't forget to add a safe rule to the model rules:

public function rules()
    return [
        [['myVirtualAttribute'], 'safe'],

The reason for this is very well explained here: Yii2 non-DB (or virtual) attribute isn't populated during massive assignment?