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