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...
Par bestmomo
Nombre de commentaires : 19