1

I am using Yii2 and I have the virtual attribute notes2 (that is used by the GUI functions instead of database attribute notes):

class Order extends \yii\db\ActiveRecord
{    
    public function getnotes2() {
        return iconv('UTF-16LE', 'UTF-8', $this->notes);
    }

    public function setnotes2($value) {
        $this->notes = iconv('UTF-8', 'UTF-16LE', $value);
    }
}

In this case both the following codes $order->notes2 and $order->Notes2 calls the setter and returns the right value.

But I have to use $order->getAttributes() function and default implementation of it does not include the virtual attributes. So I tried to override this function with:

public function attributes() {
    $attributes = parent::attributes();
    $attributes['notes2'] = 'notes2';
    return $attributes;
} 

And now json_encode($order->getAttributes()) includes empty notes2 field, but $order->notes2 (obviously - this causes the notes2 field to become empty) has no value, but $order->Notes2 has value!

Why such flip-flop of the register of the first character happens? How to declare correctly the virtual field that is available in getAttributes() as well?

But the following code (instead of override of attributes())

public function getAttributes($names = null, $except = []) {
    return array_merge(['notes2'], parent::getAttributes($names, $except));
}

behaves as nothing has been overrides - both $order->notes2 and $order->Notes2 are calculated and there is no notes2 (or Notes2) inside the json_encode($order->getAttributes())

rob006
  • 21,383
  • 5
  • 53
  • 74
TomR
  • 2,696
  • 6
  • 34
  • 87
  • OK, it seems to me that I expect too much from Yii2. I.e. if I declare notes2 as attribute then my getters/setters stop working and Yii2 considers this field as coming from database, i.e. virtual attributes https://www.yiiframework.com/wiki/167/understanding-virtual-attributes-and-getset-methods can not be included in getAttributes, encode_json and similar methods. – TomR Nov 02 '18 at 12:59
  • Did you include that attribute as `safe` attribute in validation, or some other type. If not then there is your problem. – borisaeric Nov 02 '18 at 13:54
  • Of course, I also tried to include notes2 as safe attribute in validation rules, but this improved nothing. – TomR Nov 02 '18 at 13:56
  • Now I am staring to think that I should override beforeSave and afterFind methods and do notes conversion in both directions there and I should avoid notes2 completely. But there is no documentation about those events. Still searching... – TomR Nov 02 '18 at 13:59

1 Answers1

1

This case sensitivity problem is related to PHP limitation/feature - method names are case insensitive so virtual attributes provided by methods are also case insensitive - there is not difference if you define/call it as getnotes2() or getNotes2(), so there is no way to distinguish between $order->notes2 and $order->Notes2.

This works differently for regular attributes (and properties) which does not use methods and they're not affected this case-insensitivity limitation. You didn't explain what you want to achieve, but regular attributes (defined by attributes()) and virtual attributes (provided by getters and setters) are two separate things and you can't mix them up - if you define attribute in attributes() it will be stored in internal array and getter/setter will be ignored (since regular attributes has precedence over virtual attributes).

In your case you defined the same attribute twice: once as regular attribute (in attributes()) and second as virtual attribute (with getter and setter). If you use this attribute with correct case ($order->notes2), regular attribute will be used. If case is incorrect ($order->Notes2) regular attribute will not be used (since it is case sensitive and there is no Notes2 attribute) and virtual attribute will be used (since it will ignore case) as a fallback.


If the only thing you want to do is include notes2 in getAttributes() try to override getAttributes() in this way and do not touch attributes() at all:

public function getAttributes($names = null, $except = []) {
    return array_merge(
        ['notes2' => $this->getNotes2()], 
        parent::getAttributes($names, $except)
    );
}

Not that this will ignore $names and $except arguments and always return notes2 attribute.

rob006
  • 21,383
  • 5
  • 53
  • 74