Support the ongoing development of Laravel.io →
Database Eloquent
Last updated 1 year ago.
0

After some thinking and a bit of digging I found the following:

When we try to access the attribute, the __get($key) method on our Illuminate\Database\Eloquent\Model is called. This in turn calls the getAttribute($key) method, which does the following:

public function getAttribute($key)
{
    if (array_key_exists($key, $this->attributes) || $this->hasGetMutator($key)) {
        return $this->getAttributeValue($key);
    }

    return $this->getRelationValue($key);
}

So in essence, what it does is it checks if our model either has the attribute set, in which case the model is hydrated, or if we have defined a mutator. Laravel assumes that we do not use mutators on our relation fields. Since I had defined a mutator to map my value to the namespaced class, it attempted to return $this->getRelationValue($key). But since I actually hadn't yet loaded the relationship, and the model hence was not hydrated, I got null.

Having attempted to fix this with

public function getSourceTypeAttribute($type)
{
    // transform to lower case
    $type = $this->getRelationValue('source_type');
    $type = strtolower($type);

    // to make sure this returns value from the array
    return array_get($this->types, $type, $type);

    // which is always safe, because new 'class'
    // will work just the same as new 'Class'
}

I can also state that it does not yet work, but I'm one step closer.

After quite some digging around I have found the issue!

Currently, there is a buildDictionary(Collection $models) in Illuminate\Database\Eloquent\Relations\MorphTo defined as

protected function buildDictionary(Collection $models)
{
    foreach ($models as $model) {
        if ($model->{$this->morphType}) {
            $this->dictionary[$model->{$this->morphType}][$model->{$this->foreignKey}][] = $model;
        }
    }
}

In essence, what this does is take our model(s), and check if they have the attribute of $this->morphType, and $this->morphType is set in the constructor and is by default the caller method's name (in my case source), appended with _type. So, for me, it's source_type. It will then check for $model->source_type, so for me, that'd be $orderItem->source_type. It then takes this and adds to its dictionary, which results in an array like the one beneath, with the following values for demonstration purposes:

$this->morphType = 'source_type'
$model->{$this->morphType} = 'product'
$this->foreign_key = 'source_id'
$model->{$this->foreign_key} = 1 $model = OrderItem::find(1)

[
    'product' => [
        1 => [
            OrderItem {attributes}
        ]
    ]
]

I'll probably fork the laravel core and add the functionality of custom mapping of type and class to resolve my issue.

Last updated 8 years ago.
0

Sign in to participate in this thread!

Eventy

Your banner here too?

phroggyy phroggyy Joined 30 Jan 2015

Moderators

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.