De l'Ajax avec CakePHP 3

Exemple de la mise en place de fonctionnalités AJAX dans CakePHP 3 et utilisation d'un template Javascript




Introduction

Nous avons développé un outil pour gérer nos serveurs sur lesquels sont hébergés les sites de nos clients. Cet outil a pour but de superviser et piloter les hébergements ainsi que fournir à l’utilisateur d’autres informations utiles dans l’objectif final d’avoir un suivi concret de l'état de nos serveurs.

Chaque site appartient à un serveur et contient des informations propre à lui-même. Un serveur peut avoir zéro ou plusieurs sites liés.

Il est possible de changer les informations d’un site ou d’un serveur. Lorsqu’une modification est faite, nous ajoutons une opération au journal des opérations effectuées.

Ci-dessous, nous pouvons voir dans cette vidéo comment renseigner une nouvelle opération :



Dans cet article, nous présentons dans un premier temps la manière de créer un champ en fonction de la valeur d’un autre tout en restant sur la même page, c’est à dire en AJAX. Puis, pour aller plus loin nous présenterons l’utilisation du moteur de template HandleBars.

Dans notre contexte, nous avons trois tables : Un serveur, un site et une opération. Un site appartient à un serveur et est lié par serveur_id, une opération est une action faite sur un serveur et/ou sur un site, elle est liée à la table Serveurs et à la table Sites via serveur_id et site_id.

Dans notre formulaire, nous voulons ajouter une opération avec un site et un serveur. Ici l’AJAX prend forme au niveau de la sélection d’un site, c’est à dire n’afficher que les sites ayant pour serveur_id le serveur sélectionné.


Les modèles utilisés


Le formulaire d’ajout d’une opération

Au niveau de la vue, il faut mettre l’attribut “onchange” sur le champ select du “serveur” afin que la fonction JS soit appelée. De ce fait, à chaque changement de serveur, la fonction sera appelée et les sites seront mis à jour.


echo 
// Création du Formulaire grâce au BsFormHelper 
$this->BsForm->create('Operation').
    
    // Ajout du champ serveur avec en paramètre le onchange qui appelle la fonction JS 
    $this->BsForm->select(
        'serveur_id', 
        $serveurs, 
        [
            'label' => 'Serveur',
            'id'=>'select_serveur',
            'empty' => '--- Selectionner un serveur ---', 
            'onchange'=>'selectSite()'
        ]
    ).

   // Ajout d'une zone où sera le champ select site, initialisé avec un message pour inviter
   // l'utilisateur à choisir un serveur 
    '<div id="site_id" class="form-group" style="height: 52px;"><label class="col-md-9 col-md-offset-3">Choisissez un serveur</label></div>'.
    
    $this->BsForm->submit('Ajouter', ['class' => 'btn btn-success btn-fill']).
$this->BsForm->end();


AJAX

Dans le fichier Javascript webroot/js/app.js, on crée la fonction qui est appelée dans la vue (onchange). Elle fait elle-même appel à une méthode du SitesController pour récupérer la liste des sites ayant pour serveur_id celui sélectionné dans le champ Serveur :


function selectSite() {

    // Récuperation de l'idServeur selectionné
    var idServeur = $('#select_serveur').val();

    // Reinitialisation des divs
    $('#site_id').html();
    $('#select_site').html('');	
       
   // Requete AJAX pour recuperer les sites ayant le serveur_id idServeur
       $.ajax({  
        //1. Paramètres généraux d’une requête AJAX
        // 2 . En cas de succès, affichage des sites 
        success: function (response) {

        	// Récuperation des sites
        	sites = response.sites;

        	// affichage des sites recupéré dans le select 
           // exemple : site['name']
            
        },
        //3. En cas d'erreur, afficher le statut et l'erreur
        error:function(jqXHR, textStatus, errorThrown){
            alert(textStatus);
            alert(errorThrown);
        }
    });
}

Penchons nous plus particulièrement sur les paramètres généraux d’une requête AJAX :

  • url : La ressource ciblée, ici le lien vers la méthode du contrôleur
  • type : Le type de la requête HTTP. ( GET, POST, UPDATE, DELETE…)
  • dataType : Le type de données à recevoir, ici, du JSON ( peut être du HTML)
  • success : function(response, statut){} : Entre les accolades contient le HTML renvoyé à la page. Nous récupérons l’argument response qui est le résultat de la requête, ici ce sont nos sites. C’est ici que l’on ajoute le contenu des sites dans nos divs préalablement créées dans notre vue.
  • error : en cas d’erreur la fonction prend en charges trois arguments : le résultat jqXHR qui est un objet AJAX créé en interne par jQuery, le statut, et l'erreur.

Pour voir tout le fichier JS cliquez ici.


Controller CakePHP 3

Tout d’abord, dans la fonction initialize de AppController, il est utile de charger le component RequestHandler de cette manière : “$this->loadComponent('RequestHandler');”. Le component Request Handler est utilisé dans CakePHP pour obtenir des informations supplémentaires au sujet des requêtes HTTP. Ici nous l’utilisons pour informer le controller que c’est un processus AJAX.

Dans un second temps, dans le contrôleur SitesController, il est nécessaire d’ajouter la fonction getSites qui prend en paramètre serveur_id :



class SitesController extends AppController
{
  public function getSites($idServeur)
  {
     // Si c'est une requête AJAX
     if($this->request->is('ajax')) {
     
         // Force le controller à rendre une réponse JSON.
         $this->RequestHandler->renderAs($this, 'json');
         // Définit le type de réponse de la requete AJAX
         $this->response->type('application/json');

         // Find pour récupérer les sites avec le bon serveur 
         $response = $this->Sites->find('list')->where(['serveur_id'=>$idServeur]);

         // Chargement du layout AJAX
         $this->viewBuilder()->layout('ajax');

         // Créer un contexte sites à renvoyer 
         $this->set('sites',$response);
 
         // Généreration des vues de données
         $this->set('_serialize', ['sites']);
     }
 }
}


Aller plus loin pour gérer ses templates avec Javascript et HandleBars

Dans notre contexte, l’application devait pouvoir être internationale. La fonction de cakePHP __() permet d’internationaliser le contenu du site.

Notre problème était donc que du contenu était généré dans des fonctions javascript, il était donc compliqué de mettre la fonction PHP sur ce texte.

Afin de gérer notre contenu et épurer nos fichiers JS nous avons alors entrepris d’utiliser la librairie Handlebars.

Handlebars est une librairie JS qui permet la construction de vues dynamiques, son action est de fusionner un template html avec des données récupérées par une fonction JS.


Template

Tout d’abord, il faut créer un template. Il sera lié au JS où l’on récupérera les données à afficher. Pour plus de clarté, dans vos éléments créer un dossier template ou vous mettrez tous vos templates JS. Nous rangeons par exemple le fichier suivant dans /src/Template/Element/templates/affichageSite.ctp :

<script id="templateSite" type="text/x-handlebars-template">
    {{!-- Si aucun site  n'est recupéré --}}
    {{#if emptySites}}
         <label class="col-md-9 col-md-offset-3">
             <?php echo __('Aucun site créé pour le   moment') ?>
         </label>
    {{!--  Sinon, select de tous les sites --}}
    {{else}}
        <label class="control-label col-md-3" for="select_serveur">Site</label>
        <div class="col-md-9">
         <select name="site_id" id="select_site" class="form-control">
            <option value=""><?php echo '--- ' . __('Selectionner un site') . ' ---'; ?></option>
            {{#each sites}}
                {{!--  option avec comme index l'id du site et comme value le name du site --}}
                <option value="{{@key}}">{{this}}</option>
            {{/each}}
         </select>
        </div>
    {{/if}}
</script>

Javascript

Dans la requête ajax, au niveau du traitement de la réponse success, remplacer le code par ce dernier afin de lier le template JS et les données récupérer par la fonction du controller vu précédemment :

// Variable data : objet des données passé en parametre à la fonction de Handlebars
var data = {sites: response.sites, emptySites: response.sites.length <= 0};
// Récuperation du template par son id 
var source = $("#templateSite").html();
// Compiler le template
var template = Handlebars.compile(source);
// Assigner à la div resultat le contenu
$("#site_id").html(template(data));

Vue

Dans la vue, il suffit d’ajouter le chargement du template créé ci-dessus et celui des JS (Ne pas oubliez de charger le app.js s’il n’est pas chargé avant) :

echo
$this->Html->script([‘app.js',../assets/handlebars/handlebars.min.js'], ['block' => 'scriptBottom']) .
$this->Element('templates/affichageSite');

Attention : Bien charger le template avant le fichier Javascript sinon le template compilera avec un contenu vide.


Conclusion

Nous avons vu dans cet article comment améliorer l'expérience utilisateur pour la saisie d'un champ qui dépend d'un autre. Pour cela, nous avons créé facilement un champ en AJAX avec CakePHP.

Nous avons aussi créer un template Javascript grâce a la librairie HandleBars afin de pouvoir utiliser ensuite des fonctions PHP dans les template JS aisément.

N'hésitez pas à proposer d'autres façons d'intégrer ce genre de champs ou d'autres idées concernant les templates JS.

Et si vous avez des remarques ou d’autres idées d’une manière générale, n'hésitez pas non plus à utiliser la zone de commentaires ci-dessous.

Article rédigé par Dania Kaakati durant son stage de l'été 2016.





comments powered by Disqus





Vous avez un projet ? Contactez-nous.

Agence web basée à Rennes, nous avons aussi l'habitude de travailler à distance avec des gens situés à Paris, Lyon, Nantes, etc. Donc peu importe votre localisation ou l'avancement dans la réflexion de votre projet, laissez-nous vos coordonnées et nous vous recontacterons très vite pour en parler plus en détails.


4 bis allée du Bâtiment, 35000 Rennes
06.47.52.18.78     a.weill@webandcow.com