Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Yii2. Adding attribute and rule dynamically to model

Tags:

php

yii

yii2

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

      parent::init();
    }

    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?

like image 636
Eduardo Avatar asked Jul 13 '16 16:07

Eduardo


1 Answers

Add Dynamic Attributes to a existing 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 Attributes

When all attributes are dynamic and you don't need a database. Then use the new 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 Attributes

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?

like image 188
devOp Avatar answered Oct 22 '22 19:10

devOp