t6n's avatar
Level 1

Define model attributes in trait?

Users can upload images. Later on, I need to access the images in several views. To do so flexibly, I want to be able to access an image's URL as if it were a regular attribute fetched from the model's table.

this works:

class imageUpload extends \Eloquent {
    protected $fillable = ["filename", "user_id"];

    protected $table = 'imageUploads';

    public $storageDirectory = '/uploads/userImages/';

    // fuglify
    protected $appends = ["fileUrl", "thumbUrl"];
    public function getFileUrlAttribute()
    {
        return $this->storageDirectory.$this->filename;
    }
    public function getThumbUrlAttribute()
    {
        return $this->storageDirectory."thumb/thumb_".$this->filename;
    }
}

However, there are different types of images (represented by different models), so I would like to add this functionality to all of those models. The logical way seemed to press the $appends and the two added functions into a trait, but when I do that and command the model to use that trait, I receive:

Illuminate\Database\Eloquent\Model and myTraitsName define the same property ($appends) in the composition of imageUpload. However, the definition differs and is considered incompatible. Class was composed

How can I define such attributes in a model?

0 likes
11 replies
bestmomo's avatar

Hello,

Why not to make a class that extends Eloquent with this functionnality and get inheritance for Models that need it ?

2 likes
Mushr00m's avatar

Did you solve your issue ? I need to do the same thing and have the same error while trying to overwritte the $appends variable in a Trait.

RemiC's avatar

You can't overload a class attribute with a trait.

2 likes
Mushr00m's avatar

@RemiC : I came to the same result... From my understanding it's because of the order on composition. But I can't realy understand why. The Trait methods and variables are not just "paste" (to simplify) into the class that call it ? Any insight would help my brain ^^

bestmomo's avatar

It's because of precedence rule :

"The precedence order is that members from the current class override Trait methods, which in turn override inherited methods."

1 like
pmall's avatar

@bestmomo It is better in a trait because the image upload is just a random feature. it can go alongside other. It does not define what the class is.

RemiC's avatar

My main guess would be that trait is not considered as inheritance, therefore deciding which attribute would prevails wouldn't be clear.

JarekTkaczyk's avatar

The precedence rule applies to methods, not properties. Properties conflicts cause strict or fatal error, depending on the compatibility of the property.

Reliable way is a method, that is unlikely to be overriden in the model. In this case you could use this:

protected function getArrayableAppends()
{
    $this->appends = array_unique(array_merge($this->appends, ['fileUrl', 'thumbUrl']));

    return parent::getArrayableAppends();
}
16 likes
nyce's avatar

I can see why it would be set like this, but it is somewhat frustrating. The reason one might want to insert a Trait into an extending Class, is precisely in order to override the default settings from parent-classes. This of course is only necessary in situations where inheritance is not possible - but those situations do exist as witnessed by contributors to this thread.

ather's avatar

@jarektkaczyk what a clever way to achieve the functionality. Thanks, man, you saved my day...

Snowy's avatar

man, you are Genius. Thank you sooo much

Please or to participate in this conversation.