Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

zefman's avatar

Eloquent performance improvements -- studly caps

Hello,

So I have recently been experimenting with blackfire.io the profiling tool on a fairly data heavy app, and noticed that there were thousands of calls to Illuminate\Support\Str::studly which is converts a string into studly case. This is obviously pretty heavy due to the string transformations.

After a little digging I found this is used in an Eloquent Model's hasGetMutator, a function that is called whenever an attribute from a model is retrieved to check to see if you have created a mutator.

        /**
     * Determine if a get mutator exists for an attribute.
     *
     * @param  string  $key
     * @return bool
     */
    public function hasGetMutator($key)
    {
        return method_exists($this, 'get'.studly_case($key).'Attribute');
    }

In the app I'm profiling we never use get mutators ( all models pass through a dedicated transformer at some point ) so this is a complete waste. Solution? Extend the standard eloquent model and override the hasGetMutator method like so:

public function hasGetMutator($key)
{
    return false;
}

Doing just this has shaved about 400ms of my response times on the most data heavy parts of the app, sometimes more.

Can anyone see any possible unexpected side effects from doing this? Maybe it would be a good idea to be able to set a flag on the eloquent model to enable and disable this feature.

0 likes
7 replies
JarekTkaczyk's avatar

@zefman Nice finding.

Personally I wouldn't worry about that in a heavy-data app, since Eloquent, or any ORM for that matter, is rather not good performance-wise and I would not use it at all (or at least leverage just some of its features).

I suppose Doctrine would behave better. If you have a chance to profile it with the same data, would be great.

And to answer your question: appends -> toJson/toArray will be affected, that's all I can think of. Obviously if you don't use accessors, because otherwise it will matter.


Also please try this - I wonder how much it will reduce the response time:

    public function hasGetMutator($key)
    {
        return method_exists($this, 'get'.$key.'Attribute');
    }
zefman's avatar

Yes I know Eloquent is not ideal in this situation. By data heavy I mean the app is aggregating a feed built up of a number of different models and their relations. Performance wise it is still ok at the even on a 5$ a month digital ocean testing server.

If I get chance I'll try with Doctrine too, will be interesting to compare.

acasar's avatar

@zefman Interesting find indeed. What's even more interesting is, that Eloquent models actually have mutator cache, it's just not used in this case, which is strange.

You could try changing hasGetMutator like this:

public function hasGetMutator($key)
{
    return in_array($key, static::$mutatorCache[get_class()]);
}

Sadly $mutatorCache is not an associative array, which would make this check even faster. This could be a good pull request to the framework if you have time to do some more profiling :)

thepsion5's avatar

Yep, part of the price for eloquent's convenience is that behind the scenes it's doing a lot of transforming, checking if methods exist, etc.

An alternative is to forgo the use of eloquent altogether and use the combination of a database repository, entity transformer Class, and POPO entity classes with your own setters and getters, which shouldn't be hard as long as your models aren't too heavy on relationships. I've wanted to do that for a few projects but I haven't had the chance yet.

zefman's avatar

@anzze I didn't notice that cache, strange its not being used here. Do you reckon its just an oversight? Yes I was thinking about a pull request, should be easy now that I can see that cache is already there.

@thepsion5 Yeah I would love to drop eloquent, but unfortunately there are quite a lot of relations going on. Until the performance becomes an issue I think the benefits outweigh the negatives.

1 like
acasar's avatar

@zefman The cache was first implemented for a different purpose (exporting models to array). It probably just didn't occur to anyone that it could also be easily used here.

zefman's avatar

Ah ok interesting. I'm excited to see what performance difference this little change might make.

Please or to participate in this conversation.