[go: up one dir, main page]

Skip to content

Error with attribute mutators

Bug Report

Q A
PHP version 7.2
Package version 2.x
Framework Laravel
Framework version 7.x

Actual Behaviour

When having an attribute mutator on an model and refering to an other attribute of the model instance I get an 'undefined index {column name}' error

Expected Behaviour

Have all the other model attributes of the intsance correctly loaded and available

Steps to Reproduce

As I found out the problem is this code here in Ledger.php->getProperty

if (Str::startsWith($key, 'recordable_')) {
   return $this->getFormattedProperty($this->getRecordableInstance(), \mb_substr($key, 11), $value);
}

In the method $this->getRecordableInstance() it initializes an empty shell of the morphed model and further in getFormattedProperty it searches for possible attribute mutators and applies them.

In my case I have the following mutator

    public function getAddressAttribute()
    {
        switch (strtoupper($this->type)) {
            case 'ADDR':
                return json_decode($this->attributes['address']);
                break;
            default:
                return $this->attributes['address'];
        }
    }

Yes, I know, it isn't pretty to have json and non json values in a column but the problem here is, that because of the "empty shell" the actual model gets never retrieved from the database and so $this->attributes gets never populated (also $this->type is always null). Therefore $this->attributes is an empty array with no 'address' key and the 'undefined index' error gets triggered,

Relevant information, logs and/or screenshots

none

Possible Solutions

A quick fix could be in Ledger.php->getProperty to check for the return value null and if this is the case instead returns the contents of the variable $value like this

$formatted = $this->getFormattedProperty($this->getRecordableInstance(), \mb_substr($key, 11), $value);
return $formatted !== null ? formatted : $value;

I think null maybe a common mutator return value so the quick fix will most likely not be perfect for all scenarios.

An alternate solution could be to define a constant in the Ledger model like this (or somewhere else ;) )

public const USE_RAW_VALUE = 'accountant_ledgers_use_raw_value';

In the mutator I could then use this

    public function getAddressAttribute()
    {
        if (! $this->exists) {
            return Ledger::USE_RAW_VALUE;
        }

        switch (strtoupper($this->type)) {
            case 'ADDR':
                return json_decode($this->attributes['address']);
                break;
            default:
                return $this->attributes['address'];
        }
    }

And in Ledger.php->getProperty this

$formatted = $this->getFormattedProperty($this->getRecordableInstance(), \mb_substr($key, 11), $value);
return $formatted !== self::USE_RAW_VALUE ? formatted : $value;
Edited by Markus Hebenstreit
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information