Formation PUB030 : Laravel, 2019 Les appels AJAX en Laravel

35.1 Effectuer un appel AJAX de type POST sous Laravel (illustré avec « j'aime »)


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 :

  • L'élément qui suscite l'appel AJAX sera placé dans un formulaire.
  • Il doit y avoir un jeton anti-CSRF.
  • L'URL utilisé lors de l'appel AJAX doit être absolu. Une bonne façon de fournir un URL absolu consiste à travailler avec l'attribut action du formulaire.

Pour illustrer un appel AJAX de type POST, nous allons coder une fonctionnalité qui permet d'ajouter une mention « j'aime » à un produit.

Définition de la route

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.

Fichier routes.php

// route pour l'appel AJAX 

Route::post('produits/jaime/{id}', [

    'as' => 'produits.jaime',

    'uses' => 'ProduitsController@jaime',

]);

Ajustements dans la vue

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.

Vue produits.index (Blade)

{{-- 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

Méthode d'action appelée par AJAX

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.

Différentes techniques pour faire référence à la route

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 :

  • appeler la route par son nom à l'aide de la fonction d'aide route() (méthode à privilégier) :
    PHP

    <form method="post" action="{{ route('produits.jaime', [$produit->id]) }}" >

  • entrer directement l'URL qui correspond à cette route (méthode moins intéressante) :
    PHP

    <form method="post" action="/produits/jaime/{{ $produit->id }}" >

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.

jQuery

$.ajax({

    type: "POST",

    url: "/produits/jaime/produit_id",

    ...

})

...;

Méthode d'action

Voici donc le code de la méthode d'action qui sera exécutée par notre appel AJAX.

Contrôleur Laravel (PHP)

/**

 * 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'));

}

Appel AJAX

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.

jQuery

$(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é

Veuillez noter que le contenu de cette fiche vous est partagé à titre gracieux, au meilleur de mes connaissances et sans aucune garantie.
Merci de partager !
Soumettre