mecjos's avatar

Laravel Appended Accessor Causes Slow Response

Hi. An appended accessor makes very slow the response (about 30 seconds.).. I'll try to explain with codes below..

My problematic eloquent model is :

$machines = Machine::where($condition)->with(['jobs' => function ($query) { $query->orderBy('queue', 'asc');
    },'jobs.part.image', 'jobs.project.customer', 'jobs.state.stateLang', 'operator.image'])->get();

eager loaded project relation in my model jobs.project.customer has appended accesssor as follow:

Project Model:

    protected $appends = ['stats'];

    public function getStatsAttribute()
     {
         return (new ProjectController)->stats($this->id);
     }

I use that attribute to attach project progress for every project record. Actually I don't need that in my problematic collection but it gets. And my function in which I calculated stats is as follow:

public function stats($id)
{
    $totalJobs = 0;
    $totalCompletedJobs = Job::where('project_id', $id)->where('state_id', '!=', 4)->sum('completed');
    foreach (Job::where('project_id', $id)->where('state_id', '!=', 4)->cursor() as $job) {
        $part = Part::find($job->part_id);
        $totalJobs = $totalJobs + $part->quantity;
    }
    return ['totalJobs' => $totalJobs, 'totalCompletedJobs' => (int)$totalCompletedJobs];
}

Is that method wrong to get stats in that appended attribute way? Besides, actually I don't need project stats in that $machine collection, when getting project.customer relation it takes that accessor also. How Can I do that in correct way? Can I cancel appended attribute for once for any query?

Edit: Additionally, this works very fast (around 300ms) on localhost. On shared server it takes about 30 seconds. Is that difference in possible range?

0 likes
10 replies
Snapey's avatar

you have a complex query that you run every time you list a project. In addition, this query runs multiple queries. if you install something like debugbar you will probably see 100's or possibly 1000's of sql requests. (n+1 issues)

Better to extract this to a function that runs infrequently (or only when the job us updated then commits the new stats to a separate table or caches them.

mecjos's avatar

When I remove appended accessor from model it works normal.. Might be a problem on my appended accessor? I don't think it's because of too many queries.

Snapey's avatar

but your accessor performs multiple nested queries !!!!!! It gets Job and Part models inside loops - a typical n+1 error, except you probably have n+1 *(n+1)

get some debugging on the job!

mecjos's avatar

My app is vue spa and my laravel is an api server.. I guess I can't use debugbar directly for my app.. I don't want to make complecated arrangements for debugging. If you know that problem because of too many query and my usege of appended accessors, could you give alternative solution if you know please?

Snapey's avatar

write a simple endpoint, with a view, that you can use to test your functions

mecjos's avatar

@snapey thanks for your advice.. but I already know which part of code makes slow my response.. I look for a solution for that.. If you know a solution please help... I debugged it already by myself.. It's obvious that appended accessor with relation is problematic..

Sinnbeck's avatar

Yes you need to move that whole logic into a relation instead. Then use with and withSum to load it instead. Or at least eagerload the data and iterate over that instead

mecjos's avatar

It's already eager loading but very slow again.anyway, It seems I will need to remove appended accessor.. Thanks all..

Please or to participate in this conversation.