Lorsque le code JavaScript doit fournir des variables au code serveur, les appels AJAX seront plus sécuritaires s'ils utilisent la méthode POST.
Pour effectuer un tel appel AJAX sous Laravel, il faut respecter les conditions suivantes :
Pour illustrer un appel AJAX de type POST, nous allons coder une fonctionnalité qui permet d'ajouter une mention « j'aime » à un produit.
Puisqu'un appel AJAX consiste à exécuter du code serveur à partir du client, il faut définir une route qui mènera à la méthode d'action qui doit être exécutée via AJAX.
Dans cet exemple, la fonction PHP associée à l'appel AJAX a besoin d'un seul paramètre : l'identifiant du produit. Nous allons utiliser un paramètre dans la route pour passer cette valeur.
// route pour l'appel AJAX
Route::post('produits/jaime/{id}', [
'as' => 'produits.jaime',
'uses' => 'ProduitsController@jaime',
]);
Puisque nous souhaitons effectuer un appel AJAX de type POST, il faudra prendre soin de placer dans un formulaire l'élément qui générera l'appel AJAX.
Dans cet exemple, c'est un clic sur une image qui génèrera l'appel AJAX.
{{-- TODO : ajouter un test pour désactiver la fonctionnalité si l'usager a déjà voté pour ce produit --}}
@if (Auth::check())
{{-- le formulaire utilise la route qui mène à la méthode d'action qui sera appelée par AJAX --}}
<form method="post" action="{{ route('produits.jaime', [$produit->id]) }}">
@csrf
<img class="jaime" src="{{ asset('medias/commun/jaime.png') }}" alt="J'aime" title="J'aime" />
<span id="nombrejaime">{{ $nombreJaime }}</span>
</form>
@endif
Tous les éléments sont maintenant en place pour définir la méthode d'action qui sera appelée par AJAX.
Important : c'est la route, identifiée dans notre exemple par l'attribut action du formulaire, qui fait le lien entre l'appel AJAX et le code PHP à exécuter.
Ainsi, pour un appel AJAX qui utilise la route identifiée plus haut, c'est la méthode d'action jaime() du contrôleur ProduitsController qui sera exécutée.
Dans le code PHP, il est possible de faire référence à une route par son nom ou encore par son URL. Le fait d'appeler une route par son nom offre plus de flexibilité et doit être la technique privilégiée.
Ainsi, si vous utilisez l'attribut action du formulaire pour spécifier l'URL, vous avez deux choix :
<form method="post" action="{{ route('produits.jaime', [$produit->id]) }}" >
<form method="post" action="" >
Notez qu'il aurait également été possible d'ignorer l'attribut action du formulaire et d'écrire l'URL directement dans l'appel AJAX. Ceci n'aurait cependant pas permis d'appeler la route par son nom puisque le code JavaScript ne peut pas utiliser la fonction d'aide route(). Cette technique est donc moins intéressante.
$.ajax({
type: "POST",
url: "",
...
})
...;
Voici donc le code de la méthode d'action qui sera exécutée par notre appel AJAX.
/**
* Ajoute une mention "J'aime" à un produit.
* Méthode appelée via AJAX
*
* @return \Illuminate\Http\JsonResponse Chaîne JSON avec variables reussi et aimeDeja
*/
public function jaime(int $id) : JsonResponse
{
$reussi = true;
// enregistre la mention J'aime, à condition que l'usager authentifié n'ait pas déjà une mention J'aime pour ce produit
try {
// si le produit n'existe pas, tombera immédiatement dans le catch
$produit = Produit::findOrFail($id);
// doit vérifier si a déjà voté car un usager pourrait modifier le code côté client...
$aimeDeja = Jaime::where('produit_id', $id)->where('usager_id', Auth::id())->count();
if (!$aimeDeja) {
// todo : ajouter un test pour s'assurer que l'usager détient les droits pour voter pour ce produit (pourrait avoir changé l'identifiant dans le code HTML)
$jaime = new Jaime(['produit_id'=>$id, 'usager_id'=>Auth::id()]);
$jaime->save();
}
}
catch (\Illuminate\Database\Eloquent\ModelNotFoundException $e) {
...
$reussi = false;
}
catch(\Throwable $e) {
\Log::error("Erreur inattendue. ", [$e]);
$reussi = false;
}
return response()->json(compact('reussi', 'aimeDeja'));
}
Finalement, on écrira l'appel AJAX. Ce code peut être placé directement dans une balise <script> au bas de la vue (le gabarit de base aura prévu un @yield() à l'endroit approprié) ou encore dans un fichier .js qui sera référé par une balise <script>, elle aussi au bas de la vue.
$(function () {
$('img.jaime').click(function (event) {
event.preventDefault(); // peu d'utilité ici mais serait nécessaire si l'appel AJAX était généré par un clic sur un bouton
// formulaire dans lequel la balise qui a généré l'appel AJAX était placée
var $formulaire = $(this).parents("form:first");
var donneesFormulaire = $formulaire.serialize(); // on ne lit aucune donnée dans le formulaire mais on a besoin du jeton anti-CSRF
var actionFormulaire = $formulaire.attr('action'); // l'URL pour l'appel AJAX doit être absolu
// on prend tout de suite la référence au span qui contient le nombre à incrémenter car après l'appel AJAX, $(this) ne pointera plus sur l'image
var $nombreJaime = $(this).next();
// appel AJAX
$.ajax({
type: "POST",
url: actionFormulaire,
dataType: "json",
data: donneesFormulaire
})
.done(function (response, textStatus, jqXHR) {
if (response.reussi) {
if (!response.aimeDeja) {
// incrémente le nombre à l'écran
$nombreJaime.html(Number($nombreJaime.html()) + 1 );
}
else {
afficherPopupAvertissement('Vous aviez déjà voté pour ce produit.');
}
}
else {
afficherPopupErreur('Oups, un problème a empêché l\'ajout de votre vote.');
}
})
.fail(function (jqXHR, textStatus, errorThrown) {
// le message est volontairement différent du précédent pour aider à cibler la cause de l'erreur
afficherPopupErreur('Nous sommes désolés, il n\'est pas possible de compléter l\'opération pour l\'instant.');
});
});
});
Remarquez que dans ce code, la variable $formulaire débute par un signe $ pour indiquer qu'il s'agit d'une variable jQuery.
Remarquez également qu'on aurait pu donner un id au formulaire et le retrouver comme ceci : var $formulaire = $("#leformulaire");
▼Publicité