Sorting and searching a foreign entity field on a Livewire table

Hello. I’ve just implemented a Livewire data table based on the Caleb’s tutorial on Laracasts. Everything works as expected when working with just one model. However I’m stuck trying to apply sorting and searching to a foreign model field.

User model:

    namespace App;

    use Illuminate\Contracts\Auth\MustVerifyEmail;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    use Illuminate\Notifications\Notifiable;

    use Spatie\Permission\Traits\HasRoles;

    class User extends Authenticatable
    {
        use Notifiable;

        use HasRoles;


        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
        protected $fillable = [
            'name', 'email', 'password',
        ];

        /**
         * The attributes that should be hidden for arrays.
         *
         * @var array
         */
        protected $hidden = [
            'password', 'remember_token',
        ];

        /**
         * The attributes that should be cast to native types.
         *
         * @var array
         */
        protected $casts = [
            'email_verified_at' => 'datetime',
        ];


        /**
        * Get the user's email verified status.
        *
        * @param  string  $value
        * @return string
        */
        public function getEmailVerifiedAtAttribute($value)
        {
            if ($value == null) {
                return "No";
            }
            return $value;
        }

        public function getRoleAttribute()
        {
            if($this->roles()->count() > 0) {
                return $this->roles()->first()->name;
            } else {
                return "Usuario registrado";
            }
        }

        public static function search($query)
        {
            return empty($query) ? static::query()
                : static::where('name', 'like', '%'.$query.'%')
                    ->orWhere('email', 'like', '%'.$query.'%');
        }

    }

Tried adding another orWhere() clause to the search() method in some ways. None worked. Now I left just the default ones.

Livewire controller:

namespace App\Http\Livewire\Users;

use Livewire\Component;
use Livewire\WithPagination;

class Table extends Component
{
    use WithPagination;

    public $perPage;
    public $sortField;
    public $sortAsc;
    public $search;

    public function mount()
    {
        $this->perPage = 10;
        $this->sortField = 'name';
        $this->sortAsc = true;
        $this->search = '';
    }

    public function sortBy($field)
    {
        if ($this->sortField === $field) {
            $this->sortAsc = ! $this->sortAsc;
        } else {
            $this->sortAsc = true;
        }

        $this->sortField = $field;
    }

    public function updatingPerPage()
    {
        $this->resetPage();
    }

    public function render()
    {
        return view('livewire.users.table', [
            'users' => \App\User::search($this->search)
                ->with('roles')
                ->orderBy($this->sortField, $this->sortAsc ? 'asc' : 'desc')
                ->paginate($this->perPage),
        ]);
    }
}

Livewire view:

    <div>
        <div class="row mb-4">
            <div class="col form-inline">
                Mostrar &nbsp;
                <select wire:model="perPage" class="form-control form-control-sm custom-select custom-select-sm">
                    <option>10</option>
                    <option>100</option>
                    <option>1000</option>
                </select>
                &nbsp; registros
            </div>

            <div class="col-sm-3">
                <input wire:model="search" class="form-control form-control-sm" type="text" placeholder="Buscar usuarios...">
            </div>
        </div>

        <div class="table-responsive mb-4" >
            <div class="table-header">
                <table class="table table-sm text-nowrap" role="grid">
                    <thead>
                        <tr>
                            <th width="30%">
                                <a wire:click.prevent="sortBy('name')" role="button" href="#">
                                    Nombre
                                    @include('partials._sort-icon', ['field' => 'name'])
                                </a>
                            </th>
                            <th width="30%">
                                <a wire:click.prevent="sortBy('email')" role="button" href="#">
                                    Correo electrónico
                                    @include('partials._sort-icon', ['field' => 'email'])
                                </a>
                            </th>
                            <th width="30%">
                                <a wire:click.prevent="sortBy('')" role="button" href="#">
                                    Rol
                                    @include('partials._sort-icon', ['field' => ''])
                                </a>
                            </th>
                            <th></th>
                        </tr>
                    </thead>
                </table>
            </div>
            <div class="table-body">
                <table class="table table-sm table-hover text-nowrap" role="grid">
                    <tbody>
                        @foreach ($users as $user)
                            <tr>
                                <td width="30%">{{ $user->name }}</td>
                                <td width="30%">{{ $user->email }}</td>
                                <td width="30%">{{ $user->role }}</td>
                                <td>
                                    <form method="POST" action="{!! route('backend.users.user.destroy', $user->id) !!}" accept-charset="UTF-8">
                                        <input name="_method" value="DELETE" type="hidden">
                                        {{ csrf_field() }}

                                        <div class="btn-group btn-group-xs float-right" role="group">
                                            @can('users.show')
                                                <a href="{{ route('backend.users.user.show', $user->id ) }}" class="btn btn-outline-default btn-xs" title="{{ trans('users.show') }}">
                                                    <i class=" fas fa-fw fa-eye" aria-hidden="true"></i>
                                                </a>
                                            @endcan
                                            @can('users.edit')
                                                <a href="{{ route('backend.users.user.edit', $user->id ) }}" class="btn btn-outline-default btn-xs" title="{{ trans('users.edit') }}">
                                                    <i class=" fas fa-fw fa-pencil-alt" aria-hidden="true"></i>
                                                </a>
                                            @endcan
                                            @can('users.destroy')
                                                <button type="submit" class="btn btn-outline-default btn-xs" title="{{ trans('users.delete') }}" onclick="return confirm(&quot;{{ trans('users.confirm_delete') }}&quot;)">
                                                    <i class=" fas fa-fw fa-trash-alt" aria-hidden="true"></i>
                                                </button>
                                            @endcan
                                        </div>

                                    </form>
                                </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table>
            </div>
        </div>



        <div class="table-footer">
            <div class="text-muted">
                Showing {{ $users->firstItem() }} to {{ $users->lastItem() }} out of {{ $users->total() }} results
            </div>
            <div>
                {{ $users->links() }}
            </div>
        </div>

    </div>

Tried also some ways to pass an argument to the sortBy() and [‘field’ => ‘’] on the role column. Now I left them with empty strings.

I know, this issue probably is more related to Laravel than Livewire, but I’ll really appreciate any help.

Update:

Solved using the Laravel Query Builder instead of Eloquent.

PD. Caleb, although you still make me suffer from time to time, you really did a great job creating Livewire. Thanks a lot.