Dans le précédent article, nous avons créé la liste des menus accompagnés de leurs sous-menus éventuels. On a aussi ajouté les formulaires pour créer un menu ou un sous-menu. Nous pouvons aussi supprimer l'un d'eux à partir de la liste. Enfin, et c'est ce qui a nécessité un codage un peu plus précis, nous pouvons réorganiser l'ordre des menus et sous-menus pour obtenir l'affichage souhaité sur le site. Maintenant, nous allons créer les formulaires qui vont permettre de modifier un menu ou un sous-menu existant.
Pour rappel, la table des matières est ici.
Un composant pour modifier un menu
On a à nouveau besoin d'un composant Volt pour gérer la modification d'un menu :
php artisan make:volt admin/menus/edit --class
On va ajouter la route pour l'atteindre (réservée aux administrateurs) :
Route::middleware('auth')->group(function () {
...
Route::middleware(IsAdminOrRedac::class)->prefix('admin')->group(function () {
...
Route::middleware(IsAdmin::class)->group(function () {
...
Volt::route('/menus/{menu}/edit', 'admin.menus.edit')->name('menus.edit');
On doit prévoir d'ajouter le lien dans la liste (admin.index) :
@if ($menu->order < $menus->count())
<x-popover>
...
</x-popover>
@endif
<x-popover>
<x-slot:trigger>
<x-button icon="c-arrow-path-rounded-square" link="{{ route('menus.edit', $menu->id) }}"
Et voici le code du composant :
<?php
use App\Models\Menu;
use Illuminate\Validation\Rule;
use Livewire\Attributes\{Layout, Title};
use Livewire\Volt\Component;
use Mary\Traits\Toast;
new #[Title('Edit menu'), Layout('components.layouts.admin')]
class extends Component {
use Toast;
public Menu $menu;
public string $label = '';
public ?string $link = null;
public function mount(Menu $menu): void
{
$this->menu = $menu;
$this->fill($this->menu);
}
public function save(): void
{
$data = $this->validate([
'label' => ['required', 'string', 'max:255', Rule::unique('menus')->ignore($this->menu->id)],
'link' => 'nullable|regex:/\/([a-z0-9_-]\/*)*[a-z0-9_-]*/',
]);
$this->menu->update($data);
$this->success(__('Menu updated with success.'), redirectTo: '/admin/menus/index');
}
}; ?>
<div>
<x-header title="{{ __('Edit a menu') }}" separator progress-indicator>
<x-slot:actions class="lg:hidden">
<x-button icon="s-building-office-2" label="{{ __('Dashboard') }}" class="btn-outline"
link="{{ route('admin') }}" />
</x-slot:actions>
</x-header>
<x-card>
<x-form wire:submit="save">
<x-input label="{{ __('Title') }}" wire:model="label" />
<x-input type="text" wire:model="link" label="{{ __('Link') }}" />
<x-slot:actions>
<x-button label="{{ __('Save') }}" icon="o-paper-airplane" spinner="save" type="submit"
class="btn-primary" />
</x-slot:actions>
</x-form>
</x-card>
</div>
Rien de bien nouveau dans ce code. Voici l'apparence du formulaire :
Vérifiez la validation et la sauvegarde effective.
Un composant pour modifier un sous-menu
On a besoin d'un autre composant Volt pour gérer la modification d'un sous-menu :
php artisan make:volt admin/menus/editsub --class
On va aussi ajouter la route pour l'atteindre (réservée aux administrateurs) :
Volt::route('/submenus/{submenu}/edit', 'admin.menus.editsub')->name('submenus.edit');
On doit prévoir aussi d'ajouter le lien dans la liste (admin.index) :
@if ($submenu->order < $menu->submenus->count())
<x-popover>
...
</x-popover>
@endif
<x-popover>
<x-slot:trigger>
<x-button icon="c-arrow-path-rounded-square" link="{{ route('submenus.edit', $submenu->id) }}"
Et voici le code du composant (on va évidemment réutiliser le composant qu'on a créé précédemment pour le formulaire ainsi que le trait) :
<?php
use App\Models\Submenu;
use Livewire\Attributes\{Layout, Title};
use Livewire\Volt\Component;
use Mary\Traits\Toast;
use App\Traits\ManageMenus;
new #[Title('Edit Submenu'), Layout('components.layouts.admin')]
class extends Component {
use Toast, ManageMenus;
public Submenu $submenu;
public string $sublabel = '';
public string $sublink = '';
public int $subPost = 0;
public int $subPage = 0;
public int $subCategory = 0;
public int $subOption = 1;
public function mount(Submenu $submenu): void
{
$this->submenu = $submenu;
$this->sublabel = $submenu->label;
$this->sublink = $submenu->link;
$this->search();
}
public function saveSubmenu($menu = null): void
{
$data = $this->validate([
'sublabel' => ['required', 'string', 'max:255'],
'sublink' => 'required|regex:/\/([a-z0-9_-]\/*)*[a-z0-9_-]*/',
]);
$this->submenu->update([
'label' => $data['sublabel'],
'link' => $data['sublink'],
]);
$this->success(__('Menu updated with success.'), redirectTo: '/admin/menus/index');
}
}; ?>
<div>
<x-header title="{{ __('Edit a submenu') }}" separator progress-indicator>
<x-slot:actions class="lg:hidden">
<x-button icon="s-building-office-2" label="{{ __('Dashboard') }}" class="btn-outline"
link="{{ route('admin') }}" />
</x-slot:actions>
</x-header>
<x-card>
@include('livewire.admin.menus.submenu-form')
</x-card>
</div>
Vous devriez obtenir ce menu :
Vérifiez que tout fonctionne correctement.
Le menu du pied de page
On a aussi un menu au bas de la page, essentiellement destiné à mener à des pages statiques du CSM. À ce niveau, nous n'avons pas besoin de sous-menus, ce qui va simplifier le codage. D'autre part, on va réutiliser du code déjà vu précédemment.
Un composant pour la liste
On a à nouveau besoin d'un composant Volt pour gérer la liste des menus de bas de page et le code PHP qui va faire tout le traitement :
php artisan make:volt admin/menus/footers --class
On va ajouter la route pour l'atteindre (encore réservée aux administrateurs) :
Volt::route('/footers/index', 'admin.menus.footers')->name('menus.footers');
On ajoute un item dans la barre latérale (admin.sidebar) :
@if (Auth::user()->isAdmin())
<x-menu-sub title="{{ __('Menus') }}" icon="m-list-bullet">
...
<x-menu-item title="{{ __('Footer') }}" link="{{ route('menus.footers') }}" />
Une traduction :
"Footer": "Pieds de page",
Et voici le code complet du composant :
<?php
use App\Models\{Footer};
use Illuminate\Support\Collection;
use Livewire\Attributes\{Layout, Validate, Title};
use Livewire\Volt\Component;
use Mary\Traits\Toast;
new #[Title('Footer Menu'), Layout('components.layouts.admin')]
class extends Component {
use Toast;
public Collection $footers;
#[Validate('required|max:255|unique:footers,label')]
public string $label = '';
#[Validate('nullable|regex:/\/([a-z0-9_-]\/*)*[a-z0-9_-]*/')]
public string $link = '';
public function mount(): void
{
$this->getFooters();
}
public function getFooters(): void
{
$this->footers = Footer::orderBy('order')->get();
}
public function up(Footer $footer): void
{
$previousFooter = Footer::where('order', '<', $footer->order)
->orderBy('order', 'desc')
->first();
$this->swap($footer, $previousFooter);
}
public function down(Footer $footer): void
{
$previousFooter = Footer::where('order', '>', $footer->order)
->orderBy('order', 'asc')
->first();
$this->swap($footer, $previousFooter);
}
public function deleteFooter(Footer $footer): void
{
$footer->delete();
$this->reorderFooters();
$this->getFooters();
$this->success(__('Footer deleted with success.'));
}
public function saveFooter(): void
{
$data = $this->validate();
$data['order'] = $this->footers->count() + 1;
$newFooter = Footer::create($data);
$this->footers->push($newFooter);
$this->success(__('Footer created with success.'));
}
private function swap(Footer $footer, Footer $previousFooter): void
{
$tempOrder = $footer->order;
$footer->order = $previousFooter->order;
$previousFooter->order = $tempOrder;
$footer->save();
$previousFooter->save();
$this->getFooters();
}
private function reorderFooters(): void
{
$footers = Footer::orderBy('order')->get();
foreach ($footers as $index => $footer) {
$footer->order = $index + 1;
$footer->save();
}
}
}; ?>
<div>
<x-header title="{{ __('Footer') }}" separator progress-indicator>
<x-slot:actions class="lg:hidden">
<x-button icon="s-building-office-2" label="{{ __('Dashboard') }}" class="btn-outline"
link="{{ route('admin') }}" />
</x-slot:actions>
</x-header>
<x-card>
@foreach ($footers as $footer)
<x-list-item :item="$footer" no-separator no-hover>
<x-slot:value>
{{ $footer->label }}
</x-slot:value>
<x-slot:sub-value>
{{ $footer->link }}
</x-slot:sub-value>
<x-slot:actions>
@if ($footer->order > 1)
<x-popover>
<x-slot:trigger>
<x-button icon="s-chevron-up" wire:click="up({{ $footer->id }})" spinner />
</x-slot:trigger>
<x-slot:content class="pop-small">
@lang('Up')
</x-slot:content>
</x-popover>
@endif
@if ($footer->order < $footers->count())
<x-popover>
<x-slot:trigger>
<x-button icon="s-chevron-down" wire:click="down({{ $footer->id }})" spinner />
</x-slot:trigger>
<x-slot:content class="pop-small">
@lang('Down')
</x-slot:content>
</x-popover>
@endif
<x-popover>
<x-slot:trigger>
<x-button icon="c-arrow-path-rounded-square" link="{{ route('footers.edit', $footer->id) }}"
class="text-blue-500 btn-ghost btn-sm" spinner />
</x-slot:trigger>
<x-slot:content class="pop-small">
@lang('Edit')
</x-slot:content>
</x-popover>
<x-popover>
<x-slot:trigger>
<x-button icon="o-trash" wire:click="deleteFooter({{ $footer->id }})"
wire:confirm="{{ __('Are you sure to delete this footer?') }}" spinner
class="text-red-500 btn-ghost btn-sm" />
</x-slot:trigger>
<x-slot:content class="pop-small">
@lang('Delete')
</x-slot:content>
</x-popover>
</x-slot:actions>
</x-list-item>
@endforeach
</x-card>
<br>
<x-card class="" title="{{ __('Create a new footer') }}">
<x-form wire:submit="saveFooter">
<x-input label="{{ __('Title') }}" wire:model="label" />
<x-input type="text" wire:model="link"
label="{{ __('Link') }} ({{ __('I.e.') }}: /{{ __('my-page') }}, /pages/slug {{ __('or') }} /pages/{{ strtolower(__('Folder')) }}/{{ __('my_page') }}-1)" />
<x-slot:actions>
<x-button label="{{ __('Save') }}" icon="o-paper-airplane" spinner="save" type="submit"
class="btn-primary" />
</x-slot:actions>
</x-form>
</x-card>
</div>
Quelques traductions :
"Edit a footer": "Modifier le pied de page",
"Footer deleted with success.": "Pied de page supprimé avec succès.",
"Create a new footer": "Créer un nouveau pied de page",
"Are you sure to delete this footer?": "Etes-vous sûr de vouloir supprimer ce menu de pied de page ?",
Je ne vais pas détailler le code parce qu'il est très proche de celui qu'on a mis en œuvre pour les autres menus.
Un composant pour modifier un menu de bas de page
On a à nouveau besoin d'un composant Volt pour la modification d'un menu de base de page :
php artisan make:volt admin/menus/editfooter --class
On va ajouter la route pour l'atteindre (réservée aux administrateurs) :
Volt::route('/footers/{footer}/edit', 'admin.menus.editfooter')->name('footers.edit');
On ajoute le lien dans la liste des menus (menus.footers) :
<x-button icon="c-arrow-path-rounded-square" link="{{ route('footers.edit', $footer->id) }}"
class="text-blue-500 btn-ghost btn-sm" spinner />
Voici le code :
<?php
use App\Models\Footer;
use Illuminate\Validation\Rule;
use Livewire\Attributes\{Layout, Title};
use Livewire\Volt\Component;
use Mary\Traits\Toast;
new #[Title('Edit Footer'), Layout('components.layouts.admin')]
class extends Component {
use Toast;
public Footer $footer;
public string $label = '';
public string $link = '';
public function mount(Footer $footer): void
{
$this->footer = $footer;
$this->fill($this->footer);
}
public function save(): void
{
$data = $this->validate([
'label' => ['required', 'string', 'max:255', Rule::unique('footers')->ignore($this->footer->id)],
'link' => 'regex:/\/([a-z0-9_-]\/*)*[a-z0-9_-]*/',
]);
$this->footer->update($data);
$this->success(__('Footer updated with success.'), redirectTo: '/admin/footers/index');
}
}; ?>
<div>
<x-header title="{{ __('Edit a footer') }}" separator progress-indicator>
<x-slot:actions class="lg:hidden">
<x-button icon="s-building-office-2" label="{{ __('Dashboard') }}" class="btn-outline"
link="{{ route('admin') }}" />
</x-slot:actions>
</x-header>
<x-card>
<x-form wire:submit="save">
<x-input label="{{ __('Title') }}" wire:model="label" />
<x-input type="text" wire:model="link" label="{{ __('Link') }}" />
<x-slot:actions>
<x-button label="{{ __('Save') }}" icon="o-paper-airplane" spinner="save" type="submit"
class="btn-primary" />
</x-slot:actions>
</x-form>
</x-card>
</div>
Une traduction :
"Footer updated with success.": "Pied de page mis à jour avec succès",
Et ça devrait fonctionner :
Conclusion
Nous en avons terminé avec les menus, mais nous avons encore du travail, rendez-vous au prochain article !
Pour vous simplifier la vie, vous pouvez charger le projet dans son état à l’issue de ce chapitre.
Par bestmomo
Aucun commentaire