
Créer un blog – les commentaires
Nous avons dans le précédent article codé tout ce qui concerne les utilisateurs. Maintenant on va traiter les commentaires qui sont intimement liés aux articles. Il y a une action particulière pour les commentaires : le premier commentaire d’un utilisateur n’est pas immédiatement publié, il faut qu’il soit approuvé par le rédacteur de l’article ou l’administrateur. Mais c’est la même chose pour les commentaires suivants tant que l’approbation n’a pas eue lieu. En fait c’est l’utilisateur qui est approuvé plutôt que ses commentaires. On va devoir tenir compte de cette particularité dans le tableau.
Vous pouvez télécharger le code final de cet article ici.
L’approbation des utilisateurs
On va s’occuper en premier de l’approbation des utilisateurs concernant les commentaires.
Les routes
On a besoin de deux routes :
Route::prefix('admin')->group(function () { Route::middleware('redac')->group(function () { ... // Users Route::name('users.valid')->put('valid/{user}', [BackUserController::class, 'valid']); Route::name('users.unvalid')->put('unvalid/{user}', [BackUserController::class, 'unvalid']); });
Ces routes sont accessibles pour les rédacteurs.
La première est pour approuver et la seconde l’inverse, on a le droit de changer d’avis.
Le contrôleur
Dans le contrôleur UserController dont on dispose déjà on ajoute les deux méthodes :
public function valid(User $user) { $user->valid = true; $user->save(); return response()->json(); } public function unvalid(User $user) { $user->valid = false; $user->save(); return response()->json(); }
Ces méthodes seront utilisées en Ajax, on retourne une réponse JSON. La seule action est de mettre à jour la colonne valid dans la table. Je n’ai pas jugé utile de protéger ces deux méthodes. Un rédacteur pourrait envoyer une requête pour agir sur le commentaire de l’article d’un autre rédacteur. Mais comme les lecteurs peuvent laisser des commentaires sur tous les articles la protection aurait peu d’intérêt.
Les commentaires
Passons maintenant aux commentaires.
Les routes
Pour les routes c’est simple, on a une ressource et on va se passer de show, create et store. D’autre part on a besoin d’une route supplémentaire pour les nouveaux commentaires :
Route::prefix('admin')->group(function () { Route::middleware('redac')->group(function () { ... // Comments Route::resource('comments', BackResourceController::class)->except(['show', 'create', 'store']); Route::name('comments.indexnew')->get('newcomments', [BackResourceController::class, 'index']); });
La configuration
Les titres
Dans app/config/titles on ajoute les titres pour les commentaires :
'comments' => [ 'index' => 'Comments', 'indexnew' => 'New Comments', 'edit' => 'Comment Edit', ],
Le menu
Dans app/config/menu on ajoute les items pour les commentaires :
'Comments' => [ 'icon' => 'comment', 'role' => 'redac', 'children' => [ [ 'name' => 'All comments', 'role' => 'redac', 'route' => 'comments.index', ], [ 'name' => 'New comments', 'role' => 'redac', 'route' => 'comments.indexnew', ], [ 'name' => 'fake', 'role' => 'redac', 'route' => 'comments.edit', ], ], ],
On doit avoir du nouveau dans le menu :
Le tableau
On crée la Datatable :
php artisan datatables:make Comments
<?php namespace App\DataTables; use App\Models\Comment; use Yajra\DataTables\Html\Column; use Yajra\DataTables\Services\DataTable; use Illuminate\Support\Facades\Route; class CommentsDataTable extends DataTable { use DataTableTrait; public function dataTable($query) { return datatables() ->eloquent($query) ->editColumn('user', function($comment) { return $comment->user->name; }) ->editColumn('approval', function($comment) { if($comment->user->valid) { return $this->button( 'users.unvalid', $comment->user->id, 'warning', __('Disapprove'), 'thumbs-down', 'valid' ); } return $this->button( 'users.valid', $comment->user->id, 'success', __('Approve'), 'thumbs-up', 'valid' ); }) ->editColumn('post', function($comment) { return '<a href="' . route('posts.display', $comment->post->slug) . '" target="_blank">' . $comment->post->title . '</a>'; }) ->editColumn('created_at', function ($comment) { return formatDate($comment->created_at) . __(' at ') . formatHour($comment->created_at); }) ->editColumn('action', function ($comment) { return $this->button( 'comments.edit', $comment->id, 'warning', __('Edit'), 'edit' ). $this->button( 'comments.destroy', $comment->id, 'danger', __('Delete'), 'trash-alt', __('Really delete this comment?') ); }) ->rawColumns(['approval','created_at', 'post', 'action']) ->setRowClass(function ($comment) { return $comment->user->valid ? '' : 'alert-warning'; }); } public function query(Comment $comment) { // Show only redactor posts comments $query = isRole('redac') ? $comment->whereHas('post.user', function ($query) { $query->where('users.id', auth()->id()); }) : $comment->newQuery(); if(Route::currentRouteNamed('comments.indexnew')) { $query->has('unreadNotifications'); } return $query->with('user:id,name,valid', 'post:id,title,slug'); } public function html() { return $this->builder() ->setTableId('comments-table') ->columns($this->getColumns()) ->minifiedAjax() ->dom('Blfrtip') ->lengthMenu(); } protected function getColumns() { return [ Column::computed('user')->title(__('Author')), Column::computed('approval')->title(__('Approval'))->addClass('align-middle text-center'), Column::make('body')->title(__('Comment')), Column::computed('post')->title(__('Answer to')), Column::make('created_at')->title(__('Date')), Column::computed('action')->title(__('Action'))->addClass('align-middle text-center'), ]; } protected function filename() { return 'Comments_' . date('YmdHis'); } }
Vous devriez maintenant avoir le tableau :
Pour le moment les seuls boutons qui fonctionnent sont ceux de la suppression.
L’approbation
Quand on clique sur le bouton d’approbation on doit envoyer une requête en Ajax au serveur, alors on va ajouter un peu de Javascript à la vue back.shared.index :
(() => { ... const validElement = async e => { e.preventDefault(); fetch(e.target.getAttribute('href'), { method: 'PUT', headers: headers }) .then(response => { if (response.ok) { document.location.reload(); } else { Swal.fire({ icon: 'error', title: '@lang('Whoops!')', text: '@lang('Something went wrong!')' }); } }); } ... // Set listeners window.addEventListener('DOMContentLoaded', () => { ... wrapper('table', 'click', validElement, `e.target.matches('[data-name="valid"]')`); }); })()
Pour repérer les boutons d’approbation on ajoute un attribut data-name qui nous permet de filtrer le click. On récupère l’url au niveau du bouton et on utilise fetch pour générer la requête. Au retour si c’est bon on recharge la page parce que le changement peut avoir un impact au niveau de plusieurs commentaires s’ils sont issus du même utilisateur et ça serait un peu lourd de traiter ça en Javascript. En cas d’erreur on affiche un message.
Le bouton a donc deux aspects possibles. D’autre part pour bien repérer les commentaires à approuver on met un bon fond jaune bien visible :
Au passage je me rends compte qu’il manque une traduction dans resources/lang/fr.json :
"Disapprove": "Désapprouver",
Modifier un commentaire
La validation
On ajoute la form request :
php artisan make:request Back\CommentRequest
On a un seul champ à traiter :
<?php namespace App\Http\Requests\Back; use Illuminate\Foundation\Http\FormRequest; class CommentRequest extends FormRequest { public function authorize() { return true; } public function rules() { return ['body' => 'required|max:2000']; } }
La vue
Pour la modification d’un commentaire on ajoute une vue :
@extends('back.layout') @section('main') <form method="post" action="{{ route('comments.update', $comment->id) }}"> @method('PUT') @csrf <div class="row"> <div class="col-md-12"> <x-back.validation-errors :errors="$errors" /> @if(session('ok')) <x-back.alert type='success' title="{!! session('ok') !!}"> </x-back.alert> @endif <x-back.card type='info' :outline="true" title=''> <x-back.input title='Contenu' name='body' :value='$comment->body' input='textarea' :required="true"> </x-back.input> </x-back.card> <button type="submit" class="btn btn-primary">@lang('Submit')</button> </div> </div> </form> @endsection
On retrouve les composants que l’on a déjà bien utilisés.
Vérifiez que tout fonctionne bien : validation, modification…
Conclusion
Avec les commentaires on a traité toute la partie la plus délicate du blog. Il ne nous restera plus que les contacts, les pages et les liens sociaux et sans doute des choses que j’ai dû oublier au passage mais que les plus vigilants me signaleront…


19 commentaires
DIM
Salut Best j’espère que ça va , j’ai un souci pour afficher la datatable des commentaires j’ai cette erreur :
Method App\Http\Controllers\Front\CommentController::comments does not exist. cela arrive quand je clique sur le sous menu » tous les commentaires ».
bestmomo
Salut, oui tout va bien en vacances 🙂
Est-ce que tu as la fonction comments dans le contrôleur CommentController ?
Si elle y est, essaie un composer dumpautoload.
DIM
Bonjour best content pour toi et j’espère que tu profites de ces vacances bien méritées vu ce que tu fais pour la communauté .
je n’avais pas la fonction comments mais je l’ai ajouté la voici :
public function comments(Post $post)
{
$comments = $post->validComments()
->latest()
->get();
return [
‘html’ => view(‘front.comment’, compact(‘comments’))->render(),
];
}
mais quand je veux afficher ma datatables , j’ai le message 404 : not found
bestmomo
Salut, j’ai un peu mieux regardé le code de ce blog que j’avais un peu oublié. En fait, on a un contrôleur de ressource ResourceController avec une méthode index. Ça gère tous les tableaux, y compris celui des commentaires. La méthode comments du contrôleur CommentController est destinée à afficher les commentaires dans le front sous l’article, pas dans l’administration.
bensa
Salut,
Svp je veux renvoyer la valeur de &comment->user->name à une autre fonction définie dans le helper y a il une façon pour le faire ?
Merci
bestmomo
Salut,
Je ne comprends pas ce que tu veux faire…
bensa
Salut
En fait je veux récupérer la valeur de l’utilisateur qui a commenté un article dans une fonction définie dans le helpers.php pour l’utiliser dans le package laravel file manager
bestmomo
Dans un helper tu mets tout le code que tu veux, même des requêtes à la base de données.
bensa
Salut
Oui je comprends, mon souci est de récupérer la valeur &comment->user->id dans une fonction définie dans le helpers.php afain de l’utiliser en tant que variable
bestmomo
Il faut voir le contexte, cette fonction sera appelée à partir d’où ? Selon le cas on peut transmettre des données utiles…
bensa
Il faut voir le contexte, cette fonction sera appelée à partir d’où ? Selon le cas on peut transmettre des données utiles…:
cette fonction sera appelée à partir de la fonction userField() dans la classe ConfigHandler de laravel file manager, en fait en cliquant sur un lien je veux diriger laravel file manager à ouvrir le dossier du user qui a commenté le lien.
bensa
Salut,
J’ai résolu le problème en passant l’id user dans la session.
Merci beaucoup
bestmomo
Salut,
C’est pas très joli mais ça fonctionne toujours ça 🙂
bensa
Salut j’espère que vous allez bien BESTMOMO 🙂
En fait sur Firefox, je reçois une exception : « DataTables warning: table id=comments-table – Exception Message:
Trying to get property ‘slug’ of non-object ».
dans CommentsDataTable l’expression : $comment->post->slug genère cette erreur pourtant sur Chrome ca fonctionne bien, y a il un moyen pour résoudre ce problème?
merci
bestmomo
Salut,
Il faudrait voir si c’est pas une extension du navigateur qui perturbe Ajax.
oksam
Bonjour Best! est-ce normal que lorsqu’on approuve ou le contraire, tous les commentaires du même auteur sont validésau lieu de celui sur lequel on a uniquement cliqué?
bestmomo
Salut,
Oui c’est normal puisque la validation se fait sur l’utilisateur. C’est ce qu’on a prévu dans la base.
bensa
Salut,
Comment peut on configurer la validation de l’utilisateur par commentaire?
bestmomo
Salut,
C’est un peu compliqué parce qu’il faut modifier les tables et ensuite toute la gestion des données.