Laravel 11

Albums – Les utilisateurs

La galerie est désormais bien avancée, offrant aux utilisateurs la possibilité de gérer des albums personnels, modifier leur profil et ajuster les caractéristiques de leurs images. Dans ce chapitre, nous allons nous pencher sur la mise en place d’un outil d’administration pour la gestion des utilisateurs. En fait, nous ne gérerons pas grand-chose, mais simplement afficher un tableau récapitulatif de tous les utilisateurs avec leurs principales caractéristiques. Nous profiterons de cette occasion pour approfondir les fonctionnalités des tableaux avec MaryUI.

Notre objectif est de créer une interface conviviale et fonctionnelle qui permet aux administrateurs de consulter rapidement les informations essentielles concernant les utilisateurs. Pour ce faire, nous exploiterons les capacités flexibles de MaryUI en matière de création de tables et d’affichage de données.

Un composant

Quand on a installé MaryUI il y a eu la création d’un composant pour les utilisateurs :

Nous n’avons pas créé de route pour y accéder, alors faisons-le :

Route::middleware(Admin::class)->group(function () {
    Volt::route('users', 'users.index')->name('users.index');
    ...
});

On ajoute l’item dans le menu dans le composant de navigation :

@admin
    <x-menu-sub title="{{__('Administration')}}" icon="o-cog-6-tooth">
        <x-menu-item title="{{__('Manage categories')}}" icon="o-tag" link="{{ route('categories.index') }}" />
        <x-menu-item title="{{__('Add a category')}}" icon="o-plus-circle" link="{{ route('categories.create') }}" />
        <x-menu-item title="{{__('Manage users')}}" icon="o-user" link="{{ route('users.index') }}" />
    </x-menu-sub>            
@endadmin

Avec la traduction :

"Manage users": "Gestion des utilisateurs"

Voilà ce qu’on obtient :

Des données ont été créées à l’installation de MaryUI pour alimenter ce tableau. Remarquez qu’on peut trier par ordre croissant les noms et l’âge. D’autre part, il y a un système complet de filtrage.

On adapte

On va utiliser cette bonne base pour la gestion de nos utilisateurs. Voilà le nouveau code :

<?php

use App\Models\User;
use Livewire\Volt\Component;
use Mary\Traits\Toast;
use Livewire\WithPagination; 
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;

new class extends Component 
{
    use Toast, WithPagination;

    public string $search = '';
    public bool $drawer = false;
    public array $sortBy = ['column' => 'name', 'direction' => 'asc'];

    public function clear(): void
    {
        $this->reset();
        $this->resetPage();
        $this->success(__('Filters cleared.'), position: 'toast-bottom');
    }

    public function delete(User $user): void
    {
        $user->delete();
        $this->warning("$user->name deleted", __('Good bye!'), position: 'toast-bottom');
    }

    public function headers(): array
    {
        return [
            ['key' => 'name', 'label' => __('Name')],
            ['key' => 'email', 'label' => 'E-mail'],
            ['key' => 'created_at', 'label' => __("Registration")],
            ['key' => 'images_count', 'label' => __('Images count')],
            ['key' => 'adult', 'label' => __('Adult')],
        ];
    }

    public function users(): LengthAwarePaginator
    {
        return User::query()
        ->withCount('images')
        ->when($this->search, fn(Builder $q) => $q->where('name', 'like', "%$this->search%"))
        ->orderBy(...array_values($this->sortBy))
        ->paginate(5);
    }

    public function updated($property): void
    {
        if (! is_array($property) && $property != "") {
            $this->resetPage();
        }
    }

    public function with(): array
    {
        return [
            'users' => $this->users(),
            'headers' => $this->headers()
        ];
    }
}; ?>

<div>
    <!-- HEADER -->
    <x-header title="{{__('Users')}}" separator progress-indicator>
        <x-slot:middle class="!justify-end">
            <x-input placeholder="{{__('Search...')}}" label="{{__('Search...')}}" wire:model.live.debounce="search" clearable icon="o-magnifying-glass" />
        </x-slot:middle>
        <x-slot:actions>
            <x-button label="{{__('Filters')}}" @click="$wire.drawer = true" responsive icon="o-funnel" />
        </x-slot:actions>
    </x-header>

    <!-- TABLE  -->
    <x-card>
        <x-table :headers="$headers" :rows="$users" :sort-by="$sortBy" with-pagination>
            @scope('cell_adult', $user)
                @if($user->adult)
                    <x-icon name="o-check-circle"  />
                @endif
            @endscope
            @scope('cell_created_at', $user)      
                {{ $user->created_at->isoFormat('LL') }}
            @endscope            
            @scope('actions', $user)            
                <x-button icon="o-trash" wire:click="delete({{ $user['id'] }})" wire:confirm="{{__('Are you sure to delete this user?')}}" confirm-text="Are you sure?" spinner class="btn-ghost btn-sm text-red-500" />
            @endscope
        </x-table>
    </x-card>

    <x-drawer wire:model="drawer" title="{{__('Filters')}}" right separator with-close-button class="lg:w-1/3">
        <x-input placeholder="{{__('Search...')}}" label="{{__('Search...')}}" wire:model.live.debounce="search" icon="o-magnifying-glass" @keydown.enter="$wire.drawer = false" />

        <x-slot:actions>
            <x-button label="{{__('Reset')}}" icon="o-x-mark" wire:click="clear" spinner />
            <x-button label="{{__('Done')}}" icon="o-check" class="btn-primary" @click="$wire.drawer = false" />
        </x-slot:actions>
    </x-drawer>
</div>

On ajoute quelques traductions :

"Filters": "Filtres",
"Search...": "Recherche...",
"Reset": "Réinitialiser",
"Filters cleared.": "Filtres nettoyés.",
"Users": "Utilisateurs",
"Are you sure to delete this user?": "Voulez-vous vraiment supprimer cet utilisateur ?",
"Registration": "Inscription",
"Adult": "Adulte"

Ce qui nous donne cet aspect :

On peut trier par sur toutes les colonnes. Par défaut au chargement, c’est trié par noms. Le filtrage ne s’effectue que sur le nom.

Créer un tableau avec MaryUI

Créer un tableau avec MaryUI est vraiment simple. Il faut définir les données qu’on veut inclure :

   ...

new class extends Component 
{
    ...

    public function users(): LengthAwarePaginator
    {
        return User::query()
        ->withCount('images')
        ->when($this->search, fn(Builder $q) => $q->where('name', 'like', "%$this->search%"))
        ->orderBy(...array_values($this->sortBy))
        ->paginate(5);
    }

    public function with(): array
    {
        return [
            'users' => $this->users(),
            ...
        ];
    }

    ...

}; ?>

<div>
    ...
        <x-table :headers="$headers" :rows="$users" :sort-by="$sortBy" with-pagination>
            ...
        </x-table>
    </x-card>

    ...

</div>

Les titres de colonnes :

    ...

new class extends Component 
{
    ...

    public function headers(): array
    {
        return [
            ['key' => 'name', 'label' => __('Name')],
            ['key' => 'email', 'label' => 'E-mail'],
            ['key' => 'created_at', 'label' => __("Registration")],
            ['key' => 'images_count', 'label' => __('Images count')],
            ['key' => 'adult', 'label' => __('Adult')],
        ];
    }

    ...

    public function with(): array
    {
        return [
            ...
            'headers' => $this->headers()
        ];
    }
}; ?>

<div>
    ...
        <x-table :headers="$headers" :rows="$users" :sort-by="$sortBy" with-pagination>
            ...
        </x-table>
    </x-card>

    ...
</div>

On a la possibilité d’ajouter des classes pour styliser les colonnes, les lignes du tableau et même les cellules. Mais je suis tombé sur des bugs selon les thèmes choisis avec des styles qui ne s’appliquent pas. D’autre part, pour certains thèmes, les textes de titre deviennent illisibles. Donc, on va un peu attendre les correctifs qui vont sans doute arriver par la suite…

Pour la pagination c’est tout simple à prévoir :

...

use Illuminate\Pagination\LengthAwarePaginator;

new class extends Component 
{
    use Toast, WithPagination;

    ...

}; ?>

<div>
    ...

    <!-- TABLE  -->
    <x-card>
        <x-table :headers="$headers" :rows="$users" :sort-by="$sortBy" with-pagination>

   ...

Pour le statut adulte, plutôt que d’écrire quelque chose, j’ai préféré afficher une icône pour le seul cas où le statut est adulte. C’est tout simple à réaliser :

@scope('cell_adult', $user)
    @if($user->adult)
        <x-icon name="o-check-circle"  />
    @endif
@endscope

Je vous laisse explorer toutes les possibilités offertes. La documentation est ici.

Conclusion

Dans ce chapitre, on s’est penché sur un aspect de l’administration du site en ajoutant un tableau de gestion des utilisateurs. On est partis d’un composant déjà installé par MaryUI pour le faire fonctionner avec nos données. On a vu que la réalisation d’un tableau avec MaryUI est extrêmement simple et que les ajustements possibles sont nombreux.

Pour vous simplifier la vie, vous pouvez charger le projet dans son état à l’issue de ce chapitre.

Print Friendly, PDF & Email

Laisser un commentaire