Laravel 11

Sillo – affichage des articles et des pages

Nous allons maintenant détailler notre application en examinant la façon de présenter les articles et les pages, qui sont les éléments clés de ce genre d’application. Si les pages sont simples à gérer, car elles sont isolées, les articles nécessitent une attention particulière en raison de leurs relations avec les catégories et les séries, ainsi que la gestion des commentaires.

Pour la présentation des articles, nous devons prendre en compte les éléments suivants :

  • Affichage de la catégorie : Lors de la consultation d’un article, l’affichage de la catégorie associée est essentiel. Cela permet aux utilisateurs de connaître rapidement le contexte de l’article et de naviguer dans les articles similaires.
  • Gestion des séries : Pour les articles faisant partie d’une série, il est important d’afficher cette information et de permettre une navigation spécifique au sein de cette série. Ceci inclura l’affichage des liens vers les articles précédents et suivants de la série.
  • Gestion des commentaires : La gestion des commentaires nécessite une attention particulière. Nous devons intégrer une zone de saisie de commentaires pour les utilisateurs connectés, ainsi qu’une liste de tous les commentaires précédents.

Pour la présentation des pages, c’est plus simple, car elles sont isolées.

Les pages

On va commencer par le plus simple avec les pages. On a cette route pour l’affichage :

Volt::route('/pages/{page:slug}', 'pages.show')->name('pages.show');

Et ce composant Volt :

Avec ce simple code :

<?php

use Livewire\Volt\Component;
use App\Models\Page;

// Création d'une nouvelle classe anonyme étendant Component
new class extends Component {

    // Propriété publique pour stocker la page
    public Page $page;
  
    // Méthode de montage pour initialiser la page
    public function mount(Page $page): void
    {
        $this->page = $page;
    }

}; ?>

<!-- Vue du composant -->
<div>
    <!-- Entête de la page -->
    <x-header title="{!! $page->title !!}" />
    
    <!-- Contenu de la page -->
    <div class="relative items-center w-full px-5 py-5 mx-auto prose md:px-12 max-w-7xl">
        {!! $page->body !!}
    </div>  
</div>

Je ne détaille pas le code, on a une simple propriété $page qui contient toutes les informations de la page et on l’envoie dans la vue.

On affiche le titre et le contenu :

Les articles

Pour les articles, on a un peu plus de données à gérer. Voici la route :

Volt::route('/posts/{slug}', 'posts.show')->name('posts.show');

On ne prend pas la peine de faire une liaison pour récupérer directement le modèle parce qu’on va devoir charger pas mal d’autres informations.

Le composant Volt est ici :

La méthode mount

On a une méthode mount appelée au départ :

// Initialise le composant avec le post spécifié.
public function mount($slug): void
{
    // Instanciation d'un nouveau repository de post
    $postRepository = new PostRepository;
    
    // Récupération du post par le slug
    $this->post = $postRepository->getPostBySlug($slug);
    
    // Remplissage des propriétés next et previous du post
    $this->fill($this->post->only('next', 'previous')); 
    
    // Comptage des commentaires valides du post
    $this->commentsCount = $this->post->valid_comments_count;
    
    // Initialisation d'une collection de commentaires
    $this->comments = new Collection();
}

Elle récupère les détails de l’article correspondant au slug fourni, remplit les propriétés next et previous qui vont être utiles dans le cas d’une série, compte le nombre de commentaires valides et initialise une collection vide pour stocker les commentaires.

La récupération des données dans la base se passe dans le repository :

public function getPostBySlug(string $slug): Post
{
    $post = Post::with('user:id,name', 'category', 'serie')
                ->withCount('validComments')
                ->whereSlug($slug)
                ->firstOrFail();

    if ($post->serie_id) {
        $post->previous = $post->parent_id ? Post::findOrFail($post->parent_id) : null;
        $post->next = Post::whereParentId($post->id)->first() ?: null;
    }

    return $post;
}

On voit qu’on charge les informations sur l’auteur de l’article, la catégorie et la série éventuelle.

La partie supérieure

La partie supérieure est gérée par ce code :

<!-- Actions disponibles pour les utilisateurs authentifiés -->
<div class="flex justify-end gap-4">
    @auth
        <!-- Bouton pour modifier le post -->
        @if(Auth::user()->isAdmin() || Auth::user()->id == $post->user_id)
            <x-button icon="c-pencil-square" link="{{ route('posts.edit', $post) }}" tooltip-left="{{ __('Edit') }}" spinner class="btn-ghost btn-sm" />
            <x-button icon="o-finger-print" wire:click="clonePost({{ $post->id }})" tooltip-left="{{ __('Clone') }}" spinner class="btn-ghost btn-sm" />
        @endif
    @endauth   
    <!-- Bouton pour afficher la catégorie du post -->
    <x-button class="btn-sm"><a href="{{ url('/category/' . $post->category->slug) }}">{{ $post->category->title }}</a></x-button>
    <!-- Bouton pour afficher la série du post (s'il existe) -->
    @if($post->serie)
        <x-button class="btn-sm"><a href="{{ url('/serie/' . $post->serie->slug) }}">{{ $post->serie->title }}</a></x-button>
    @endif        
</div>

Au minimum, on affiche un bouton pour la catégorie à laquelle appartient l’article. Un clic sur ce bouton affiche les articles de cette catégorie.

On a ensuite des affichages optionnels :

  • un bouton pour modifier l’article si on est en présence de l’administrateur ou de l’auteur
  • un bouton pour cloner l’article si on est en présence de l’administrateur ou de l’auteur
  • un bouton pour la série éventuelle à laquelle appartient l’article. Un clic sur ce bouton affiche les articles de cette série.

Juste au-dessous, on trouve le titre de l’article et sa date de publication :

<!-- Titre et date du post -->
<x-header title="{!! $post->title !!}" subtitle="{{ $post->created_at->isoFormat('LL') }} "  />

Le contenu de l’article

On a ensuite le contenu de l’article géré avec ce code :

<!-- Contenu du post -->
<div class="relative items-center w-full px-5 py-5 mx-auto prose md:px-12 max-w-7xl">
    <div class="flex flex-col items-center mb-4">
        <img src="{{ asset('storage/photos/' . $post->image) }}" />
    </div>
    <br>
    {!! $post->body !!}
</div>
<br><hr>

Avec deux éléments :

  • l’image d’en-tête
  • le corps de l’article

L’auteur et le nombre de commentaires

Au-dessous, on informe sur le nom de l’auteur de l’article et le nombre de commentaires :

<!-- Informations sur l'auteur et le nombre de commentaires -->
<div class="flex justify-between">
    <p>@lang('By ') {{ $post->user->name }}</p>
    <em>
        @if($commentsCount != 0) 
            @lang('Number of comments: ') {{ $commentsCount }}
        @else
            @lang('No comments')
        @endif
    </em>
</div>

Et s’il n’y a pas de commentaires :

Les séries

Si l’article appartient à une série, il faut afficher la navigation :

<!-- Navigation entre les posts de la série -->
@if($post->serie)
    <br>
    <div class="{{ $previous ? 'flex justify-between' : 'flex justify-end' }}">
        @if($previous)
            <x-button label="{{ __('Previous') }}" icon="s-arrow-left" link="{{ url('/posts/' . $previous->slug) }}" class="btn-sm" />
        @endif
        @if($next)
            <x-button label="{{ __('Next') }}" icon-right="s-arrow-right" link="{{ url('/posts/' . $next->slug) }}" class="btn-sm" />
        @endif
    </div>        
@endif

Les commentaires

En dernier, on a la zone des commentaires. Voici le code correspondant :

<!-- Section des commentaires -->
<div class="relative items-center w-full px-5 py-5 mx-auto md:px-12 max-w-7xl">
    @if($listComments)
        <!-- Afficher les commentaires -->
        <x-card title="{{ __('Comments') }}" shadow separator >
            @foreach ($comments as $comment)
                @if(!$comment->parent_id)
                    <livewire:posts.comment :$comment :$comments :depth="0" :key="$comment->id" />
                @endif
            @endforeach
            <!-- Formulaire pour ajouter un nouveau commentaire -->
            @auth
                <livewire:posts.commentBase :postId="$post->id" />
            @endauth
        </x-card>
    @else
        <!-- Afficher le bouton pour afficher les commentaires -->
        @if($commentsCount > 0)
            <div class="flex justify-center">            
                <x-button label="{{ $commentsCount > 1 ? __('View comments') : __('View comment') }}" wire:click="showComments" class="btn-outline" />
            </div>
        <!-- Afficher le formulaire pour ajouter un commentaire si aucun commentaire n'est disponible -->
        @else
            @auth
                <livewire:posts.commentBase :postId="$post->id" />
            @endauth
        @endif            
    @endif
</div>

Au chargement de l’article, on se contente d’afficher un bouton pour charger les commentaires (s’il y en a) :

S’il n’y a pas de commentaire, on affiche directement le formulaire pour en créer un (si le visiteur est authentifié) :

Si on clique sur le bouton pour afficher les commentaires, alors ils s’affichent :

J’écrirai un article spécifique sur le sujet des commentaires qui constitue la partie la plus délicate au niveau codage.

Conclusion

Cet affichage des articles est évidemment la partie la plus importante de l’application. Je pense avoir rassemblé tous les élément sessentiels.

Print Friendly, PDF & Email

Laisser un commentaire