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

danichurras's avatar

Performance problem with auth check and loops

I have some datatables with 500-ish rows of models and some relationships loaded. I've tested and profiled my views because it was taking too long (30 seconds+) to load the listings. It ended up being my action buttons, which have authorization checks around it.

@foreach($models as $model)
<td>{{$model->someData}}</td>

<td>
@can('some.permission')
<a href="{{route('some.route', $model)}}">
<i class="fas fa-eye"></i>
</a>
@endcan
</td>
@endforeach

It makes some kind of sense because I don't need to check the same permission for every row, given that in my case I don't have permissions set to specific models. If I am allowed to edit Posts then all the posts within the list can show the button to edit it. There's nothing like ownership or something like that.

I tried to store the value of the permission in a $variable outside of the loop and it worked but it's somewhat dowdy.

Any tips? I'm running laravel 9 btw in php8.1

0 likes
8 replies
LaryAI's avatar
Level 58

One solution to this problem is to perform the authorization check outside of the loop and store the result in a variable. Then, use that variable inside the loop to conditionally display the action button. This way, the authorization check is only performed once instead of for every row in the loop.

Here's an example implementation:

@php
    $canEdit = auth()->user()->can('some.permission');
@endphp

@foreach($models as $model)
    <td>{{$model->someData}}</td>

    <td>
        @if($canEdit)
            <a href="{{route('some.route', $model)}}">
                <i class="fas fa-eye"></i>
            </a>
        @endif
    </td>
@endforeach

In this example, we're using the @php directive to perform the authorization check and store the result in the $canEdit variable. Then, inside the loop, we're using an @if directive to conditionally display the action button based on the value of $canEdit.

By performing the authorization check outside of the loop, we're reducing the number of times the check needs to be performed, which can improve performance.

Shafqat_ali's avatar

Can you show your controller code where you are fetching models because 30 sec for just 500 approx records is too much and maybe there is something in controller code that needs investigation.

danichurras's avatar

@Ali88 it's not the problem, as I said, I've profiled the code and the fetch took miliseconds to execute. The problem lies on this auth check in the view renderization. As I comment out the @cans the view suddently renders as fast as expected. I wonder if my AuthServiceProvider.php can be the guilty. Maybe?

Snapey's avatar

i would pass permission to the blade file as a flag, then check that. You might find this less 'dowdy' ?

danichurras's avatar

@Snapey that's initially how I solved that but some listings have 4-5 different action buttons, consequently I have 4-5 authorization flags in my view. Just searching for more methods to clean this up :)

danichurras's avatar

@Snapey I don't. The AuthServiceProvider file came from previous developers that were no longer in the project as I was assigned to it. Didn't really found a better way to define the gates.

$permissions = Cache::remember('permissions', config('auth.permissions_cache_ttl'), function () {
    return Permission::with('groups')->get();
});

$groups = Cache::remember('permissions_groups', config('auth.permissions_cache_ttl'), function () {
    return PermissionGroup::all()->pluck('id', 'slug');
});


// SuperUsers

Gate::before(function () use ($groups) {
    $profile_permission = session('selected_profile')->permission;
    if ($profile_permission == $groups['superadmin']) {
        Debugbar::enable();
        return true;
    }
});

foreach ($permissions as $permission) {
    if ($permission->name === 'permission_group.edit' || $permission->name === 'permission_group.remove') {
        Gate::define($permission->name, function ($user, $id) use ($permission, $groups) {
            return ($permission->groups->contains(session('selected_profile')->permission) and $id !== $groups['superadmin'] and $id !== $groups['admin']);
        });
    } elseif ($permission->name === 'step.edit' || $permission->name === 'step.remove') {
        Gate::define($permission->name, function ($user, Step $step) use ($permission) {
            return ($permission->groups->contains(session('selected_profile')->permission) && $step->type !== 'Fixed');
        });
    } else {
        Gate::define($permission->name, function ($user) use ($permission) {
            return $permission->groups->contains(session('selected_profile')->permission);
        });
    }
}

Please or to participate in this conversation.