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

zaster's avatar

Eager Loading - Livewire

Trying to minimize these queries thorugh Eager Loading(Received from laravel debugbar)

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Manager') and `roles`.`deleted_at` is null limit 1

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Manager') and `roles`.`deleted_at` is null limit 1

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Manager') and `roles`.`deleted_at` is null limit 1

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Manager') and `roles`.`deleted_at` is null limit 1

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Manager') and `roles`.`deleted_at` is null limit 1

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Manager') and `roles`.`deleted_at` is null limit 1

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Manager') and `roles`.`deleted_at` is null limit 1

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Manager') and `roles`.`deleted_at` is null limit 1

User.php

    public function roles()
    {
        return $this->belongsToMany(Role::class);
    }

Role.php

    public function users()
    {
        return $this->belongsToMany(User::class);
    }

Livewire Class

public function render()
    {
        $users = User::with('companies', 'roles')
        ->when(in_array($this->type, ['is_customer', 'is_employee',  'is_provider']), function($query){
            $query->where($this->type, true);
        })
		->where(function ($query) {
                $query->where('name', 'LIKE', '%' . $this->search . '%')
                    ->orWhere('email', 'LIKE', '%' . $this->search . '%');
        })
        ->when($this->searchCompany != '', function($query){
                $query->whereHas('companies', function($query) {
                       $query->where('company_id', $this->searchCompany);
             });
        })
        ->orderBy($this->sortBy, $this->sortAsc ? 'ASC' : 'DESC')
        ->paginate($this->perPage);

        // dd($users);

        return view('livewire.user-tracker', [
            'users' => $users,
        ]);
    }
0 likes
3 replies
SilenceBringer's avatar

@zaster it's not related to eager loading. you just perform exactly the same query many times.

And I do not see the reason for it in controller. Maybe in view?

Also - debugbar should show the line which calls this query, can you check?

idew's avatar

@zaster You can reduce the number of times the query is executed by putting $users on the class as a public property and executing the query on mount. The render method executes every time a state is changed and the view needs refreshed. Mount is called once.

public Collection $users;
...
public function mount()
{
        $this->users = User::with('companies', 'roles')
        ->when(in_array($this->type, ['is_customer', 'is_employee',  'is_provider']), function($query){
            $query->where($this->type, true);
        })
		->where(function ($query) {
                $query->where('name', 'LIKE', '%' . $this->search . '%')
                    ->orWhere('email', 'LIKE', '%' . $this->search . '%');
        })
        ->when($this->searchCompany != '', function($query){
                $query->whereHas('companies', function($query) {
                       $query->where('company_id', $this->searchCompany);
             });
        })
        ->orderBy($this->sortBy, $this->sortAsc ? 'ASC' : 'DESC')
        ->paginate($this->perPage);
}
...
public function render()
{
        return view('livewire.user-tracker');
}

Note that $users is no longer passed to the view. When it's a public property on the component it's made available to the view automatically.

zaster's avatar

@silencebringer

I think it has something to do with this

I am very new for this gates concept

AuthServiceProvider.php

<?php

namespace App\Providers;

use App\Models\User;
use App\Policies\UserPolicy;
use Illuminate\Support\Facades\Gate;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * The policy mappings for the application.
     *
     * @var array
     */
    // protected $policies = [
    //      'App\Models\User' => 'App\Policies\UserPolicy',
    // ];
    /**
     * Register any authentication / authorization services.
     *
     * @return void
     */
    public function boot()
    {
        $this->registerPolicies();

        //User Gates
        Gate::define('user-create', function($user) {
            return $user->hasAnyRoles(['User Manager', 'Super User Manager']);
        });

        Gate::define('user-read', function($user) {
            return $user->hasAnyRoles(['User Manager', 'Super User Manager']);
        });

        Gate::define('user-update', function($user) {
            return $user->hasAnyRoles(['User Manager', 'Super User Manager']);
        });

        Gate::define('user-delete', function($user) {
            return $user->hasAnyRoles(['Super User Manager']);
        });



        //Quotaiton Gates
        Gate::define('quotation-create', function($quotation) {
            return $quotation->hasAnyRoles(['Quotation Manager', 'Super Quotation Manager']);
        });

        Gate::define('quotation-read', function($quotation) {
            return $quotation->hasAnyRoles(['Quotation Manager', 'Super Quotation Manager']);
        });

        Gate::define('quotation-update', function($quotation) {
            return $quotation->hasAnyRoles(['Quotation Manager', 'Super Quotation Manager']);
        });

        Gate::define('quotation-delete', function($quotation) {
            return $quotation->hasAnyRoles(['Super Quotation Manager']);
        });



        //Item Gates
        Gate::define('item-create', function($item) {
            return $item->hasAnyRoles(['Item Manager', 'Super Item Manager']);
        });

        Gate::define('item-read', function($item) {
            return $item->hasAnyRoles(['Item Manager', 'Super Item Manager']);
        });

        Gate::define('item-update', function($item) {
            return $item->hasAnyRoles(['Item Manager', 'Super Item Manager']);
        });

        Gate::define('item-delete', function($item) {
            return $item->hasAnyRoles(['Super Item Manager']);
        });





    }
}


From Laravel Debugbar when the i run the livewire render method

select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('User Manager', 'Super User Manager') and `roles`.`deleted_at` is null limit 1
760μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Super User Manager') and `roles`.`deleted_at` is null limit 1
560μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('User Manager', 'Super User Manager') and `roles`.`deleted_at` is null limit 1
610μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Super User Manager') and `roles`.`deleted_at` is null limit 1
670μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('User Manager', 'Super User Manager') and `roles`.`deleted_at` is null limit 1
600μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Super User Manager') and `roles`.`deleted_at` is null limit 1
550μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('User Manager', 'Super User Manager') and `roles`.`deleted_at` is null limit 1
560μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Super User Manager') and `roles`.`deleted_at` is null limit 1
480μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('User Manager', 'Super User Manager') and `roles`.`deleted_at` is null limit 1
590μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Super User Manager') and `roles`.`deleted_at` is null limit 1
620μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('User Manager', 'Super User Manager') and `roles`.`deleted_at` is null limit 1
640μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Super User Manager') and `roles`.`deleted_at` is null limit 1
580μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('User Manager', 'Super User Manager') and `roles`.`deleted_at` is null limit 1
570μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Super User Manager') and `roles`.`deleted_at` is null limit 1
610μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Quotation Manager', 'Super Quotation Manager') and `roles`.`deleted_at` is null limit 1
570μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('Item Manager', 'Super Item Manager') and `roles`.`deleted_at` is null limit 1
570μs\app\Models\User.php:121pk22
select `roles`.*, `role_user`.`user_id` as `pivot_user_id`, `role_user`.`role_id` as `pivot_role_id` from `roles` inner join `role_user` on `roles`.`id` = `role_user`.`role_id` where `role_user`.`user_id` = 1 and `name` in ('User Manager', 'Super User Manager') and `roles`.`deleted_at` is null limit 1

My livewire view looks like this

<div class="flex flex-col">

    <div class="xl:flex xl:items-center xl:justify-between xl:mx-4 xl:mb-2">
        <p class="text-3xl ...">User Tracker </p>
        <div class="xl:w-3/6">
            <div class="flex flex-col space-y-2 xl:space-x-2 xl:flex xl:flex-row xl:space-y-0">
                {{-- <input wire:model="search" type="search" placeholder="Search"> --}}

                <select wire:model="perPage"
                    class="block border-gray-300 rounded-md shadow-sm 1/6 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
                    <option>10</option>
                    <option>100</option>
                    <option>1000</option>
                </select>

                <select wire:model="type"
                    class="block border-gray-300 rounded-md shadow-sm 1/6 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
                    <option value="">Select a Type</option>
                    <option value="is_customer">Customer</option>
                    <option value="is_employee">Employee</option>
                    <option value="is_provider">Provider</option>
                </select>

                <select wire:model="searchCompany"
                    class="block w-full border-gray-300 rounded-md shadow-sm focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
                    <option value="">Select a Company</option>
                    @foreach ($companies as $company)
                        <option value="{{ $company->id }}">{{ $company->name }}</option>
                    @endforeach
                </select>

                <x-jet-input wire:model="search" id="search" type="search" class="block w-full" placeholder="Search"  autocomplete="off" />
            </div>
        </div>
    </div>

    <div class="overflow-x-auto xl:-my-2 sm:-mx-6 lg:-mx-8">
        <div class="inline-block min-w-full py-2 align-middle sm:px-6 lg:px-8">
            {{-- <div class="overflow-hidden border-b border-gray-200 shadow sm:rounded-lg"> --}}
            <table class="min-w-full text-center divide-y divide-gray-200">
            <thead class="font-bold text-gray-700 bg-gray-50">
                <tr>
                {{-- <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">

                    </div>
                </th> --}}
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        <button wire:click="sortBy('id')" class="text-xs font-bold tracking-wider uppercase">Id</button>
                        <x-sort-icon icon sort-field="id" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                </th>
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        <button wire:click="sortBy('name')" class="text-xs font-bold tracking-wider uppercase">name</button>
                        <x-sort-icon icon sort-field="name" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                </th>
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        <button wire:click="sortBy('email')" class="text-xs font-bold tracking-wider uppercase">email</button>
                        <x-sort-icon icon sort-field="email" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        {{-- wire:click sorting ability taken out --}}
                        <button class="text-xs font-bold tracking-wider uppercase">Type</button>
                        <x-sort-icon icon sort-field="type" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                </th>
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        {{-- wire:click sorting ability taken out --}}
                        <button class="text-xs font-bold tracking-wider uppercase">Companies</button>
                    </div>
                </th>
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        <button wire:click="sortBy('mobile')" class="text-xs font-bold tracking-wider uppercase">mobile</button>
                        <x-sort-icon icon sort-field="mobile" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                </th>
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        <button wire:click="sortBy('phone')" class="text-xs font-bold tracking-wider uppercase">phone</button>
                        <x-sort-icon icon sort-field="phone" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                </th>
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        <button wire:click="sortBy('title')" class="text-xs font-bold tracking-wider uppercase">title</button>
                        <x-sort-icon icon sort-field="title" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                </th>
                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        <button wire:click="sortBy('first_name')" class="text-xs font-bold tracking-wider uppercase">first name</button>
                        <x-sort-icon icon sort-field="first_name" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                </th>
               <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">
                    <div class="flex items-center justify-center">
                        <button wire:click="sortBy('last_name')" class="text-xs font-bold tracking-wider uppercase">last name</button>
                        <x-sort-icon icon sort-field="last_name" :sort-by="$sortBy" :sort-asc="$sortAsc" />
                    </div>
                </th>

                <th scope="col" class="w-1/12 px-6 py-3 text-xs tracking-wider uppercase">

                </th>
                </tr>
            </thead>
            <tbody class="text-left bg-white divide-y divide-gray-200">
                @foreach ($users as $user)
                    <tr>
                        <td class="px-6 py-2 text-sm text-center text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            {{ $user->id }}
                        </td>

                        <td class="px-6 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            {{ $user->name }}
                        </td>
                        <td class="px-6 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            {{ $user->email }}
                        </td>
                        <td class="px-6 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            @if(Auth::user()->is_admin == true)
                            {{ $user->is_admin ? "Admin" : "" }}
                            @endif
                            {{ $user->is_customer ? "/ Customer" : ""}}
                            {{ $user->is_employee ? "/ Employee" : "" }}
                            {{ $user->is_provider ? "/ Provider" : "" }}
                        </td>
                        <td class="px-1 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            <div class="h-10 overflow-x-hidden overflow-y-auto">
                            @foreach ($user->companies as $company)
                                {{ $company->name }} <br>
                            @endforeach
                            </div>
                        </td>
                        <td class="px-6 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            {{ $user->mobile }}
                        </td>
                        <td class="px-6 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            {{ $user->phone }}
                        </td>
                        <td class="px-6 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            {{ $user->title }}
                        </td>
                        <td class="px-6 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            {{ $user->first_name }}
                        </td>
                        <td class="px-6 py-2 text-sm text-gray-500 border-2 border-gray-200 whitespace-nowrap">
                            {{ $user->last_name }}
                        </td>
                        <td class="py-2 text-sm text-gray-500 whitespace-nowrap">
                            @can('user-update')
                                <a href="{{ route('employees.users.edit', $user) }}"><x-warning-button>Edit</x-warning-button></a>
                            @endcan
                            @can('user-delete')
                                <x-jet-danger-button wire:click="confirmUserDeletion({{ $user->id }})">Delete</x-jet-danger-button>
                            @endcan
                        </td>
                    </tr>
                @endforeach

                <!-- More people... -->
            </tbody>
            </table>
        </div>
        {{-- Pagination --}}
         <div class="mx-4 mt-4">
            {{-- {{ $users->links() }} --}}
        </div>

    </div>

     <!-- Delete User Confirmation Modal -->
    <x-jet-dialog-modal wire:model="confirmingUserDeletion">
        <x-slot name="title">
            {{ __('Delete User') }}
        </x-slot>

        <x-slot name="content">
            {{ __('Are you sure you want to delete this User?') }}
         </x-slot>

        <x-slot name="footer">
            <x-jet-secondary-button wire:click="cancelDeleteUser()" wire:loading.attr="disabled">
                {{ __('Cancel') }}
            </x-jet-secondary-button>

            <x-jet-danger-button class="ml-2" wire:click="deleteUser({{ $confirmingUserDeletion }})" wire:loading.attr="disabled">
                {{ __('Delete') }}
            </x-jet-danger-button>
        </x-slot>
    </x-jet-dialog-modal>

</div>

Please or to participate in this conversation.