Laravel Livewire: Datatable, con campos de ordenación y busqueda mediante QueryString

- Andrés Cruz

In english

Laravel Livewire: Datatable, con campos de ordenación y busqueda mediante QueryString

Una tabla de tipo datatable, es el término que nosotros empleamos para indicar una tabla en HTML básico con los datos con las operaciones tipo CRUD para cada uno de estos datos; esta tabla; es una tabla que nos permite definir un conjunto de opciones útiles para nuestro usuario como paginación, campos de búsqueda, ordenación de columnas y por supuesto, opciones para editar, eliminar y todas las que necesites; en esta entrada solamente vamos a crear la tabla, con las opciones correspondientes para la misma empleando para nuestra interfaz, tailwind.css, aunque puedes emplear cualquier otra cosa como CSS nativo o incluso las tablas que tenemos para Bootstrap.

Un DataTable puede almacenar datos de cualquier tipo, incluyendo textos, números, fechas, imágenes entre otros tipos. Se utiliza comúnmente en aplicaciones web para mostrar datos en una tabla de forma ordenada y estructurada. Por ejemplo, si se tiene información de un listado de publicaciones, en este supuesto listado pueden aparecer las opciones para eliminar, crear, editar, ver el detalle, filtrar los post.

A la final, un Datatable es una tabla con esteroides en las cuales defines multiples opciones de gestión, filtro y ordenación.

En Laravel Livewire, gracias a todo el comportamiento reactivo y la comunicación con el servidor de manera directa desde el cliente, podemos crear este tipo de tablas muy facilmente.

Creando el Datatable en Laravel Livewire

Como mencionamos antes, hay cierto componente de gestión en las tablas, por lo tanto, debemos de definir el modelo, la vista y el componente.

Definir Modelo

Primero es lo primero, y necesitamos una fuente de datos, nuestro modelo de usuario, que es el que nos viene de gratis con Laravel Livewire, nos viene bastante bien, pero tú puedes emplear cualquier otra; en nuestro modelo de toda la vida para los usuarios con los traits que tenemos por defecto:

class User extends Authenticatable #implements MustVerifyEmail
{
    use HasApiTokens;
    use HasFactory;
    use HasProfilePhoto;
    use HasTeams;
    use Notifiable;
    use TwoFactorAuthenticatable;

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

Definir trait para los campos de búsqueda mediante queryString

Ahora vamos a nuestro componente, para eso vamos a emplear un componente de Livewire que podemos crear con:

php artisan make:livewire UserList

Y recuerda que con esto, se crean 3 elementos, nuestro Componente del lado del servidor y nuestra vista; ahora con el componente creado, tenemos que definir algunas características para poder emplear la búsqueda; empleamos la propiedad llamada queryString, para indicar que propiedades vamos a emplear para la búsqueda; esto es útil ya que si nuestro usuario refresca la página, los datos que están escribiendo el mismo quedan guardados en la URL de la aplicación y la búsqueda se mantendría:

use WithPagination;

protected $queryString = ['name'];
public $name = "";

Definir columnas y función de ordenación:

    public $sortColumn = "id";
    public $sortDirection = "asc";

    public $columns = [
        'id',
        'name',
        'email'
    ];

    public function sort($column)
    {
        $this->sortColumn = $column;
        $this->sortDirection = $this->sortDirection == 'asc' ? 'desc' : 'asc';
    }

Por lo demás, definimos algunas propiedades más para indicar las columnas que vamos a emplear, el nombre de las mismas y también una función y propiedades que nos permitirán realizar la búsqueda por columnas, y de una el tipo de ordenación, si ascendente o descendente.

Procesar data para el listado, función de render

Ahora, el corazón de nuestro componente, en el cual armamos el query o la consulta a la base de datos, así como mapear las propiedades que queramos emplear para filtrar nuestra data, que en este caso es el nombre/name; por lo demás empleamos la paginación y devolvemos a la vista que queremos emplear:

    public function render()
    {
        $users = User::orderBy($this->sortColumn, $this->sortDirection);

        if ($this->name) {
            $users->where('name', 'like', '%' . $this->name . '%')
                ->orWhere('email', 'like', '%' . $this->name . '%');
        }

        $users = $users->paginate(10);

        return view('livewire.users-list', ['users' => $users]); //->layout('layouts.app');
    }

Código completo del proyecto

Finalmente, por aquí puedes ver el código fuente del componente usado anteriormente, en la cual, tenemos las columnas, así como las columnas para indicar el filtro, ordenación, en la función de render, indicamos la vista así como aplicar los filtros correspondientes, recuerda que cada una de esas propiedades usadas en a la función de render, es utilizada a nivel de la vista como modelos en los campos, gracias a la comunicación entre el backend con el fronend, la comunicación se realiza de manera directa y este es el secreto y punto más importante de este proyecto:

namespace App\Http\Livewire;

use App\Models\User;
//use Illuminate\View\Component;
use Livewire\Component;
use Livewire\WithPagination;

class UsersList extends Component
{

    use WithPagination;

    //public $users;

    public $columns = [
        'id',
        'name',
        'email'
    ];

    public $name = "";
    public $sortColumn = "id";
    public $sortDirection = "asc";

    protected $queryString = ['name'];

    public function render()
    {
        $users = User::orderBy($this->sortColumn, $this->sortDirection);

        if ($this->name) {
            $users->where('name', 'like', '%' . $this->name . '%')
                ->orWhere('email', 'like', '%' . $this->name . '%');
        }

        $users = $users->paginate(10);

        return view('livewire.users-list', ['users' => $users]); //->layout('layouts.app');
    }

    public function sort($column)
    {
        $this->sortColumn = $column;
        $this->sortDirection = $this->sortDirection == 'asc' ? 'desc' : 'asc';
    }

    public function cleanFilter()
    {
        $this->name = "";
    }

    public function delete(User $user)
    {
        $user->delete();
    }
}

Código de la vista

La vista, es realmente sencillo; como te comentamos, estamos empleando Tailwind, tenemos un bloque de inputs referenciando las propiedades del modelo anterior mediante el wire:model, que es para definir los campos del formulario para filtrar y luego tenemos un bloque para la tabla, que al ya tener toda la lógica implementada en el componente desde Laravel, nos dedicamos a pintar la data y columnas sin ninguna condición:

<div class="flex mt-1 ">
                        <x-jet-input wire:model="name" class="block w-full" />

                        <x-jet-secondary-button class="ml-2" wire:click="cleanFilter">

                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                                    d="M3 4a1 1 0 011-1h16a1 1 0 011 1v2.586a1 1 0 01-.293.707l-6.414 6.414a1 1 0 00-.293.707V17l-4 4v-6.586a1 1 0 00-.293-.707L3.293 7.293A1 1 0 013 6.586V4z" />
                            </svg>

                        </x-jet-secondary-button>
                    </div>

                    <table class="table-auto w-full">
                        <thead>
                            <tr>

                                @foreach ($columns as $c)
                                    <th class="p-3" wire:click="sort('{{ $c }}')">
                                        <button>
                                            {{ $c }}
                                            @if ($sortColumn == $c)
                                                @if ($sortDirection == 'asc')
                                                    &uarr;
                                                @else
                                                    &darr;
                                                @endif
                                            @endif
                                        </button>
                                    </th>
                                @endforeach

                                <th class="p-3">Acciones</th>
                            </tr>
                        </thead>
                        <tbody>
                            @forelse ($users as $u)
                                <tr>
                                    <td class="border p-3">{{ $u->id }}</td>
                                    <td class="border p-3">{{ $u->name }}</td>
                                    <td class="border p-3">{{ $u->email }}</td>
                                    <td class="border p-3 flex justify-center">
                                        <x-a class="p-1 bg-blue-600 mr-1" href="{{ route('user.edit', $u) }}">
                                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
                                                stroke="currentColor">
                                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                                                    d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z" />
                                            </svg>
                                        </x-a>

                                        <x-jet-danger-button class="p-sm-button" wire:click="delete({{ $u->id }})">

                                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
                                                stroke="currentColor">
                                                <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
                                                    d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
                                            </svg>
                                        </x-jet-danger-button>
                                    </td>
                                </tr>
                            @empty
                                <tr>
                                    <td colspan="3">
                                        <p>No hay registros</p>
                                    </td>
                                </tr>
                            @endforelse

                        </tbody>
                    </table>

                    <br>

                    {{ $users->links() }}

Puntos importantes es notar el llamado a las funciones mediante los eventos de click que nos permiten llamar a funciones del componente desde la vista como la de delete o la de ordenación/sort para realizar la ordenación.

Recuerda que este es una pequeña parte de todo lo que vemos en el curso de Laravel básico en mi curso.

Código fuente del proyecto.

Andrés Cruz

Desarrollo con Laravel, Django, Flask, CodeIgniter, HTML5, CSS3, MySQL, JavaScript, Vue, Android, iOS, Flutter

Andrés Cruz En Udemy

Acepto recibir anuncios de interes sobre este Blog.