Index de l'article

Exemple d'interface cartographiqueFabrik est une (excellente) extension gratuite et open-source disponible sur Joomla, et permettant de construire des applications de bases de données. Un article présentant globalement le CCK Fabrik est déjà disponible sur ce site.

L'article ci-dessous est plus détaillé, et regroupe une série de pratiques courantes ou avancées, mais peu documentées en français. L'article reprend aussi des fonctionnalités PHP classiques mais ici adaptées à l'API Joomla.

Bien sûr cette page n'a rien d'exhaustif, et ne pourrait exister sans le forum Fabrik. Merci encore à toute l'équipe Fabrik et au projet Joomla. Et non, je ne travaille pas pour eux.


Divers

Redirections dynamiques

Pour rediriger vers une autre version d'un enregistrement en cours d'édition (après soumission d'un formulaire par exemple, vers le même enregistrement mais ailleurs), utilisez {rowid} dans l'URL pour inclure la valeur du champ id de l'enregistrement courant.

http://monsite.com/mon-lien-de-menu/details-ou-form/ID-du-formulaire/{rowid}

Ici en URL absolue pour plus de clarté, mais préférez l'URL relative.

Vous pouvez aussi utiliser cette façon de construire des URL pour faire des filtres dynamiques (voir la section sur les recherches dynamiques).

Pré-remplir des champs après une redirection (variables dans les URLS)

Exemple de lien :

href="url_relative?matable___monchamp_raw={matableprecedente___lechampaprendre_raw}"

Variables de session

Quelques variables de session Joomla (l'utilisateur connecté) pouvant être utilisées dans du code Fabrik (dans des filtres de listes, des traitements PHP d'élément, de formulaires...), afin de récupérer l'id, le name ou l'email de l'utilisateur connecté :

{$my->id}
{$my->email}
{$my->name}

Insérer des objets Fabrik dans des articles ou modules Joomla

Quelques exemples :

{ fabrik view=visualization id=ID_De_Votre_Rendu }
{ fabrik view=list id=ID_De_Votre_Liste }
{ fabrik view = details id = ID_De_Votre_Liste rowid=ID_Enregistrement }

Attention les balises Fabrik sont ici cassées pour ne pas être interprêtées par ce site Joomla/Fabrik, pensez à enlever les espaces inutiles près des accolades.

Sans oublier d'activer les Plugins de contenus dans les options du module si c'est un module.

Une page officielle plus détaillée : http://fabrikar.com/forums/index.php?wiki/content-plugin/#embedding-lists

Ouvrir un objet Fabrik dans une fenêtre popup à partir d'un lien

Cette astuce ne concerne pas directement Fabrik, mais plutôt la gestion de contenus web en général.

Les liens de menus Joomla permettent déjà d'ouvrir des objets Fabrik dans des fenêtres popups. Mais parfois on souhaite les personnaliser ou les ouvrir à partir de liens dédiés.

Exemple de lien ouvrant une liste Fabrik dans une petite fenêtre en popup :

<href="https://monsite.com/index.php?option=com_fabrik&amp;view=list&amp;listid=ID_LISTE" style="color: #8360ba;" onclick="window.open(this.href, 'NOM_POPUP', 'scrollbars=1,resizable=1,height=700,width=500,location=0');return false;">My link</a>

Modifiez l'id de la liste (ID_LISTE) et donnez un nom à votre popup (NOM_POPUP) afin de permettre l'affichage de plusieurs popups en même temps.

Utiliser une connexion BDD distante dans du code PHP

Si vous utilisez plusieurs connexions BDD, déjà créées dans l'administration Fabrik, et que vous souhaitez les utiliser dans des plugins PHP, remplacez le fameux $myDb = JFactory::getDbo(); par :

$myDb = FabrikWorker::getDbo(false, Id_De_Votre_Connexion);

Utiliser Bootstrap 3 avec Fabrik

Certains templates Joomla utilisent Bootstrap 3, cela pour notre plus grand plaisir. Ce n'est pourtant pas le cas par défaut de Fabrik (cela pour notre plus grand confort, il serait inutile la plupart du temps).

Vous pouvez donc parfois vous retrouver avec des boutons mal placés ou du CSS douteux, n'ayez crainte. Pour corriger cela, téléchargez le layout Fabrik pour Bootstrap 3 et placez-le dans votre template personnel.

https://github.com/Fabrik/Fabrik-Joomla-alt-layouts

Il s'agit simplement de copier le contenu du répertoire bootstrap3 dans templates / VOTRE_TEMPLATE / html / layouts / com_fabrik (créez si besoin les répertoires manquants).

Customiser des chekboxs

Dans un template CSS de formulaire :

.fabrikgrid_checkbox{
position: relative !important ;
}
 
.fabrikgrid_checkbox input[type="checkbox"]{
background-color: #fff !important ;
height: 30px !important;
width: 30px !important;
}
 
.fabrikgrid_checkbox span{
margin-left: 10px ;
line-height: 2.8em;
}
 
.fabrikgrid_checkbox input[type="checkbox"]:checked {
background-color: #fff !important ;
height: 30px !important;
width: 30px !important;
}

Récupération de variables

Forme des données

En fonction de la forme des données à récupérer, ou de ce que l'on souhaite en faire, il y a plusieurs façons de récupérer des variables. Quelques exemples :

$db->loadObject() // returns first row as an object. e.g. $obj->field.
$db->loadObjectList() // returns resultset as an object. For multiple records.
$db->loadResult() // returns first field of first row as a value. A single value
$db->loadRow() // returns the first row as an indexed array.
$db->loadAssoc() // returns first row as an associated array.
$db->loadAssocList() // returns resultset as an associated array. For multiple records.
$db->Execute($sql) // Executes sql that doesn't return anything. Pass sql as parameter.
$db->loadColumn() // Renvoie un tableau (array) - Faire suivre d'un - implode(',', $Results); - sera nécessaire dans bien des cas.

Récupérer des données dans un plugin formulaire

$My_Test = $formModel->formData['table___field'] ;

Ou dans certains cas (json, champ enclosé par guillemets double...) :

$My_Test = $data['table___field'] ;

Récupérer les données d'un tableau de Cron PHP plugin

Ces tableaux sont systématiquement des tableau de tableaux d'objets, et oui...

Ainsi pour en récupérer certaines données, il faudra boucler chaque tableaux contenus dans le tableau externe, puis chaque lignes de ces tableaux internes :

foreach ($data as $group) {
foreach ($group as $row) {
$Ma_Belle_Variable = $row->TABLE___CHAMP ;
 }
}
// Pour vérification
echo $Ma_Belle_Variable; exit ;

Personnaliser les formulaires

Modifier l'affichage d'une chaîne à la volée

Pratique pour modifier dynamiquement l'affichage d'un champ selon la page. Telle catégorie par exemple, peut avoir un nom légèrement différent selon la page ou les droits utilisateurs.

Dans un champ calc :

$MyCategory = '{videos___category}' ;
if ($MyCategory === 'Preview') {
$MyCategory = 'WATCH THE PREVIEW';
}
if ($MyCategory === 'Full') {
$MyCategory = 'WATCH THE FULL VIDEO';
}
if ($MyCategory === 'Academic') {
$MyCategory = 'ACADEMIC';
}

Raccourcir l'affichage d'un champ

Si le contenu de votre champ est potentiellement long, mais parfois court, et que vous souhaitez standardiser l'affichage en fonction du nombre de caractère par exemple.

Fabrik permet cela nativement, dans les options d'un champ text par exemple. Cependant il peut arriver que vous souhaitiez faire cela ailleurs (dans un champ calc par exemple) :

Dans un champ calc :

$Title = '{videos___title}' ;
 
$nbChar = 40 ; // Nb. de caractères sans '...'
if(strlen($Title) >= $nbChar)
{$ShortTitle = substr($Title, 0, $nbChar).' ...';}
else
{$ShortTitle = $Title ;}

Masquer les champs vides

Fabrik crée par défaut une classe CSS sur les champs vides (fabrikDataEmpty). En vue détail vous pouvez les masquer avec un display: none; et en vue formulaire les modifier autrement. Pour ne le faire que sur un seul de vos champs par exemple, un champ texte classique input :

.fabrikDataEmpty input#ma-table___mon-champ{
background-color: red ;
}

Masquer les sous-groupes vides (repeatable group des tables jointes)

En overridant son template, on peut masquer les sous-groupes vides des formulaires en vue détail.

Dans le cas d'un groupe affiché en mode table par exemple, modifiez ainsi son fichier components \ com_fabrik \ views \ form \ tmpl \ MyTEMPLATE \ default_repeatgroup_table.php (ajout de if (!$group->newGroup) : et de son endif;) :

  1. <?php
  2. // No direct access
  3. defined('_JEXEC') or die('Restricted access');
  4.  
  5. $group = $this->group;
  6.  
  7. if (!$group->newGroup) :
  8.  
  9. // only output if there the group is not empty
  10. if (count($group->subgroups) > 0) :
  11. ?>
  12. <legend class="legend">Orders</legend>
  13. <table class="table table-striped repeatGroupTable">
  14. <thead>
  15. <tr>
  16. <?php
  17.  
  18. // Add in the table heading
  19. $firstGroup = $group->subgroups[0];
  20. foreach ($firstGroup as $el) :
  21. $style = $el->hidden ? 'style="display:none"' : '';
  22. ?>
  23. <th <?php echo $style; ?> class="<?php echo $el->containerClass?>">
  24. <?php echo $el->label_raw?>
  25. </th>
  26. <?php
  27. endforeach;
  28.  
  29. // This column will contain the add/delete buttons
  30. if ($group->editable) : ?>
  31. <th data-role="fabrik-group-repeaters"></th>
  32. <?php
  33. endif;
  34. ?>
  35. </tr>
  36. </thead>
  37. <tbody>
  38. <?php
  39.  
  40. // Load each repeated group in a <tr>
  41. $this->i = 0;
  42. foreach ($group->subgroups as $subgroup) :
  43. $this->elements = $subgroup;
  44. echo $this->loadTemplate('repeatgroup_row');
  45. $this->i ++;
  46. endforeach;
  47.  
  48. endif;
  49.  
  50. ?>
  51. </tbody>
  52. </table>
  53. <?php
  54. endif;

Masquer un groupe selon une valeur

Dans le fichier default_repeatgroup.php d'un template dédié à votre formulaire, ajouter cette condition juste après le foreach :

  1. ...
  2. foreach ($group->subgroups as $subgroup) :
  3.  
  4. // TO HIDE A GROUP IF A FIELD city IS EQUAL TO MOSCOW
  5. // var_dump($subgroup['city']);
  6. if (array_key_exists('city', $subgroup)):
  7. // var_dump($subgroup['city']);
  8. if ($subgroup['city']->value === 'MOSCOW'):
  9. continue ;
  10. endif;
  11. endif;
  12.  
  13. ...

Créer des boutons dynamiques 

Parfois vous avez besoin de créer de nouveaux boutons, autres que les classiques Enregistrer ou Appliquer. Pour mener à un autre formulaire par exemple, lancer une recherche (voir la section sur les recherches dynamiques)...

Vous pouvez alors créer l'URL nécessaire dynamiquement (en utilisant le fameux {rowid}, voir la section sur les redirections dynamiques) pour récupèrer l'ID de l'enregistrement concerné, puis l'insérer dans un champ Calc sous forme de lien. De cette façon par exemple :

return '<a style="margin:20px" href="booking-list-management-confirm/form/28/{rowid}">Prendre la course ?</a>';

Libre à vous ensuite de le styliser, pour en faire un vrai bouton. Vous pouvez bien sûr mettre plusieurs boutons dans un seul champ Calc

Afficher des valeurs différenciées

En lecture seule, vous pouvez afficher des valeurs par couleurs, quitte à changer le type de l'élément sur le formulaire concerné uniquement. Passez en type calc, puis par exemple ici sur un booléen, afficher un texte en rouge ou en vert selon la valeur 0 ou 1 :

$MonChampBooleen = '{profiles___certificate_upload}';
 
if (($MonChampBooleen) == 1) {
// Calcul
return '<div style="text-align: center ; font-weight : bold ; color : green ; ">Authorized</div>' ;}
 
// Si vide
if (($MonChampBooleen) == 0) {
return '<div style="text-align: center ; font-weight : bold ; color : red ; ">Not allowed</div>';}

Remplacer le formulaire d'enregistrement utilisateur Joomla par défaut par un formulaire Fabrik

Fabrik peut avantageusement substituer l'enregistrement utilisateur par défaut de Joomla par un formulaire personnalisé. Vous pourrez ainsi ajouter des champs mais également des règles de valisation, déclencher des actions, envoyer des emails très personnalisés...

Pour cela il vous faudra utiliser l'excellent plugin Joomla user form plugin.

Ensuite une bonne pratique est de rediriger le lien https://myweb.site/index.php?option=com_users&view=registration vers votre formulaire Fabrik. Ceci est même obligatoire en terme de sécurité, afin de rediriger d'éventuels robots.

Pour cela modifiez ou créez l'override templates/mytemplate/html/com_users/registration/default.php et mettez-y une redirection. Ainsi par exemple :

header('Location: MyFabrikForm');
exit();
?>

MyFabrikForm est une URL relative.

Faîtes ceci pour tous vous templates frontaux même si non-utilisés. Sans cela un robot utilisant cette adresse par exemple, parviendra au formulaire par défaut de Joomla même si vous n'utilisez pas le template Protostar : https://myweb.site/index.php?option=com_users&view=registration&template=protostar

Ce n'est donc pas optionnel !


Personnaliser les tableaux (les listes selon Fabrik)

Attribuer des couleurs différenciées aux lignes

Permettez aux utilisateurs de saisir une couleur HTML dans un champ afin de l'utiliser dans l'affichage des listes. Pour créer un bord colorisé par exemple.

Dans un champ calc :

$Color = '{table___color}' ;
 
return '
<div style="height: 200px ; border-left: 5px solid '.$Color.'">
</div>
' ;

Concaténations

Concaténation dans un élément databasejoin

Il y a plusieurs manières et plusieurs endroits où faire des concaténations. Dans une liste déroulante affichant les enregistrements d'une autre table par exemple, pour être plus clair on souhaite concaténer plusieurs champs, avec du texte ou de la ponctuation.

Ici nous récupérons directement les champs, en les faisant précéder de leur table et de 3 underscores (ex : ma_table___champ1) :

ma_table___champ1,'valeur texte', ma_table___champ2, 'ponctuation'

Concaténation dans un champ calc

Dans un champ calc il faudra ajouter un peu de PHP, pour afficher un champ par exemple :

return {ma_table___champ1} ;

Ou encore, pour ajouter du texte :

return '{ma_table___champ_prix} Euros' ;

Si vous n'affichez pas un champ, mais une variable, et souhaitez tout de même ajouter du texte, c'est plus classique :

return $nbrPelican.' pélican(s)' ;

Un autre exemple où on décode au passage les caractères HTML (html_entity_decode()) pour les rendre lisibles, dans une concaténation :

$MaCivility = '{commande___civility_badge}';
$MonFirstName_badge = '{commande___firstname_badge}';
$MonLastNameBadge = '{name_badge}';
 
$MaCivility = html_entity_decode($MaCivility) ;
$MonFirstName_badge = html_entity_decode($MonFirstName_badge) ;
$MonLastNameBadge = html_entity_decode($MonLastNameBadge) ;
 
return $MaCivility.' '.$MonFirstName_badge.' '.$MonLastNameBadge ;

On peut aller plus loin, en ajoutant de la ponctuation dynamique en Javascript (pour ne pas laisser une 2ème virgule après un champ éventuellement vide par exemple...). Je n'ai plus le code sous la main, mais j'y étais parvenu grâce au forum officiel.

Concaténation dans un plugin de formulaire

Pour une raison ou une autre, ça peut aussi servir... Ce sera alors sous forme SQL :

$myQuery->select("CONCAT(firstname, ' ', lastname) AS fullname") ;

Pré-filtres dans les URLs

Filtrer par valeurs de champs

Savoir pré-filtrer des listes par simple URL peut rendre de grand service, pour faciliter des recherches utilisateur par exemple.

Un exemple pour filrer en fonction de valeurs de champs. Observez bien la construction de l'URL :

http://hg-map.fr/flux-wms-liste?resetfilters=1&wms___type=WMS&wms___etendue=Monde

  • resetfilters=1 pour vider d'éventuels filtres déjà présents sur la liste.
  • wms___type=WMS recherche les enregistrements dont le champ type est égale à WMS.
  • wms___etendue=Monde recherche les enregistrements dont le champ etendue est égale à Monde.

Ces filtres de listes se construisent par URL, autrement dit n'importe quel utilisateur peut se construire ses propres filtres personnalisés, qu'il peut ensuite ajouter à ses favoris... Faites-le savoir à vos utilisateurs, pour les rendre autonome !

Filtrer par recherche globale

Un autre exemple pour lancer une recherche globale à partir du contenu d'un champ d'un formulaire. La recherche ne se fera que dans les champs que vous avez mentionnés dans la recherche globale de la liste concernée (en back, dans Listes/{La liste concernée}/Filtres) :

http://hg-map.fr/flux-wms-liste?resetfilters=1&fabrik_list_filter_all_2_com_fabrik_2=Socioeconomic

  • resetfilters=1 pour vider d'éventuels filtres déjà présents sur la liste.
  • fabrik_list_filter_all_2_com_fabrik_2, le chiffre 2 est ici l'ID de la liste en question.
  • fabrik_list_filter_all_2_com_fabrik_2=socioeconomic, le terme socioeconomic va être recherché sur tous les champs mentionnés dans la Recherche globale de la liste.

Filtrer dynamiquement

Très utile pour lancer des recherches sur des contenus externes ou multi-bases. En les affichant sur un formulaire Fabrik avec un bouton lançant la recherche par exemple (voir la section sur les boutons dynamiques). Dans ce cas l'URL utilisée aura cette forme :

http://hg-map.fr/flux-wms-liste?resetfilters=1&amp;fabrik_list_filter_all_2_com_fabrik_2={Ma_Table___MonChamp_raw}

Contrainte/validation de champs

Fabrik propose déjà des méthodes intégrées de validation de champs (notempty, isemail, isunique, areuniques...).

Mais parfois elles sont insuffisantes, ou alors elles suffisent tout-à-fait mais pour des questions de maintenance, vous préférez les centraliser. Les écrire dans des plugins de formulaire sera alors idéal.

Gérer la casse

Bien sûr vous pouvez jouer avec les traditionnels strtolowerucfirstucwords... Mais la méthode ci-dessous est particulièrement efficace pour mettre la 1ère lettre de chaque mot en majuscule, et le reste en minuscule. Avec une bonne gestion de la ponctuation.

Dans une régle de validation PHP d'un élément Fabrik :

return mb_convert_case($data, MB_CASE_TITLE) ;

Ou dans un fichier PHP classique :

$ma_valeur = mb_convert_case($ma_valeur, MB_CASE_TITLE) ;

Caractères spéciaux

Exemple où l'on souhaite supprimer le signe + et les espaces dans un champ d'une table jointe.

Dans un plugin de formulaire, le 1er enregistrement de la table jointe va être déclaré sous la forme formData['table1___champ1'][0] (un tableau donc), puis mis-à-jour en étant appelé sous la forme telephone___code_tel1.0.

// Déclaration des caracteres interdit 
$notallowed_inphone = array(".", " ", "+") ;
 
// Déclaration et traitement du champ concerné
$var_code_tel1_0 = $formModel->formData['telephone___code_tel1'][0];
$var_code_tel1_0 = is_array($var_code_tel1_0) ? $var_code_tel1_0[0] : $var_code_tel1_0;
$var_code_tel1_0 = htmlentities($var_code_tel1_0) ;
$var_code_tel1_0 = str_replace($notallowed_inphone, "", $var_code_tel1_0);
 
// MAJ du champ concerné
$formModel->updateFormData('telephone___code_tel1.0', $var_code_tel1_0, true) ;

Encodage des accents

Utilisons htmlentities dans un plugin de formulaire.

// Déclaration et traitement du champ lastname
$var_lastname = $formModel->getElementData('customer___lastname', true);
$var_lastname = htmlentities($var_lastname) ;
 
// MAJ champ lastname
$formModel->updateFormData('customer___lastname', $var_lastname, true) ;

Limiter la taille d'affichage d'un champ texte

Ce n'est que du pur PHP mais mais parfois on souhaite limiter la taille d'affichage d'un champ texte autrement que par les options natives de Fabrik.

Dans un élément calc par exemple, on affiche ici les 35 premiers caractères, puis 3 petits points si le champ est plus long:

$Title = '{mytable___title}' ;
 
$nbChar = 35; // Nb. de caractères sans '...'
if(strlen($Title) >= $nbChar)
{$ShortTitle = substr($Title, 0, $nbChar).' ...';}
else
{$ShortTitle = $Title ;}

Désencodage de caractères

Parfois des caractères HTML sont présents dans du vocabulaire contrôlé de tables spécifiques par exemple, et la gestion de ces caractères n'est pas toujours automatisé.

Exemple dans un plugin Databasejoin, pour interprêter des caractères comme #39;&#160;... Dans l'onglet Advanced :

$opt->text = htmlspecialchars_decode($opt->text,ENT_QUOTES) ;

ou encore :

$opt->text = html_entity_decode($opt->text,ENT_QUOTES) ;

Remplir un champ selon un formulaire

L'une des façons de diriger les actions utilisateurs est de proposer la modification d'enregistrements à partir de formulaires ou listes différents (d'où nous enclencheront des traitement différents, des filtres différents...).

Un champ caché status par exemple, sera peut-être nécessaire. Et édité si l'utilisateur a utilisé ce formulaire.

Vous pouvez alors forcer un champ directement dans les options de l'élément, mais également dans un plugin de formulaire PHP :

$formModel->updateFormData('ma_table___champs_a_modifier','Nouvelle_Valeur',true);

Remplir un champ selon des données saisies dans un autre champ

À partir de l'exemple précédent, toujours dans un plugin de formulaire PHP, vous pouvez ajouter des variables contenant d'autres champs saisis par l'utilisateur, puis les utiliser dans des conditions d'un autre champ. Vous pouvez le faire en ajoutant une règle Javascript dans les options de l'élément, afin d'aider l'utilisateur pendant la saisie, mais aussi en PHP après la soumission du formulaire, pour corriger l'utilisateur a son insu :

$ma_variable = $formModel->formData['ma_table___mon_champ'];
if ($ma_variable == 'Non')
{$formModel->updateFormData('ma_table___champs_a_modifier','Nouvelle_Valeur',true);}

Sourcer les enregistrements

Identifier l'origine des enregistrements saisis dans une base de données est primordial. Bien sûr les champs de type Date de saisie et Utilisateur ayant saisi sont classiques et sans doute déjà existants dans vos applications. Cependant leur format/stockage n'est pas toujours ergonomique à la lecture, aux exports ou à certains requêtages, reporting...

De même, la définition de vos sources peut varier selon le formulaire de saisi.

Il est donc bel et bon parfois, de remplir un champ source avec disons, le nom de l'utilisateur ayant saisi par exemple (ou son id, email...), la date de saisie et éventuellement d'autres informations textuelles.

Le code suivant, dans l'option Default d'un élément de type Field, récupère puis concatène le nom de l'utilisateur Joomla en majuscule et l'année en cours :

$MonName = '{$my->name}' ;
$MonName = strtoupper($MonName) ;
return $MonName.' '.date('Y') ;

N'oubliez pas de cocher l'option Eval sous l'encart d'insertion du code, et de brider la saisie manuelle via les droits d'accès de l'élément.


Exécuter des requêtes

Dans un plugin PHP de formulaire

Il y a plusieurs façons d'exécuter des requêtes SQL en fond avec Fabrik, par exemple dans un plugin PHP de formulaire, où l'on souhaite remplacer une valeur dans une table liée par une valeur saisie en table principale.

Pratique quand vous avez été obligé de lier vos tables sur des chaînes textuelles, mais souhaitez quand même en permettre la modification. Exemple : une liste de sociétes attachées à leurs employés respectifs. Le nom de la société change, ou un utilisateur a décelé une faute d'orthographe, la modification doit également se faire sur la table des employés.

Dans ce cas décrit, la table liée permet d'attacher plusieurs enregistrements bien sûr (les employés), et renvoie donc un tableau (is_array).

$ma_var_new_field = $formModel->formData['matable1___champ1'] ;
 
$ma_var_old_field = $formModel->formData['matable2___champ2'][0];
$ma_var_old_field = is_array($ma_var_old_field) ? $ma_var_old_field[0] : $ma_var_old_field;
 
defined('_JEXEC') or die('Restricted access');
$db = FabrikWorker::getDBO(false, YourConnexionID);
 
$query = "
update table2
set champ2 = '$ma_var_new_field'
where champ2 = '$ma_var_old_field'
" ;
 
$db->setQuery($query);
$db->query();

Dans un cron PHP (scheduled task)

Ici un exemple de transfert régulier de données vers une base distante (la liste utilisée par le cron vérifie déjà d'éventuelles similitudes avec les enregistrements distants, et ne renvoie que les non-existants).

Comme les données appelées dans un cron Fabrik se trouve dans un tableau de tableaux d'objets, un double foreach sera nécessaire.

<?php
defined('_JEXEC') or die('Restricted access');
 
// Variables
foreach ($data as $group) {
foreach ($group as $row) {
$my_title = $row->table1___title ;
$my_firstname = $row->table1___firstname ;
$my_lastname = $row->table1___lastname ;
$my_speciality = $row->table1___speciality ;
$my_email = $row->table2___email_raw ;
$my_country = $row->table3___country ;
$my_code_phone1 = $row->table4___code_phone1 ;
$my_phone1 = $row->table4___phone1 ;
 
// Query
$db = FabrikWorker::getDBO(false, YourIdConnexion);
 
// Apostroph replace
$my_lastname = str_replace("'", "\'", $my_lastname);
$my_firstname = str_replace("'", "\'", $my_firstname);
...
 
// Just one query in a simple syntax in a temporary table
$query = "
INSERT INTO y_table
(civility, lastname, firstname, speciality, email, country, code_tel1, tel1)
VALUES ('$my_title', '$my_lastname', '$my_firstname', '$my_speciality', '$my_email', '$my_country', '$my_code_phone1', '$my_phone1') ;
" ;
 
$db->setQuery($query);
$db->query();
 
}}
?>

Attention

  • Si vous voulez insérer dans plusieurs tables distantes liées entre elles, il semble que Joomla/Fabrik ne le permettent pas. Utilisez alors une table temporaire puis un trigger, afin de dispatcher vos données comme souhaitée. Un exemple de trigger à cette fin ici !
  • Pensez à échapper ou à remplacer toutes vos variables susceptibles de contenir des apostrophes (juste avant la requête), sinon la requête échouera !
  • Si vous souhaitez exécuter plusieurs requêtes dans un cron (des requêtes séparées par un point-virgule) alors vous devez être en pilote MySQL PDO !

Dans un filtre de liste (Data/Pre-filter)

Il est possible de filtrer dynamiquement une liste à partir d'une requête SQL, et si besoin à partir d'une base distante ! Très pratique dans le cas de communication entre bases distantes.

Exemple ici dans une requête NOT IN, où il est nécessaire de faire un implode sur les résultats, afin de les séparer par une virgule. Les guillemets se gérent toutes seules grâce à Fabrik. Choisissez bien Eval dans le Type de requête Fabrik, sinon le code ne sera pas correctement interprêté :

// DB 2 access
$myDb = FabrikWorker::getDbo(false, YourIdConnexion) ;
 
// Recover query by email
$myQuery = $myDb->getQuery(true) ;
 
$myQuery->select('email')
->from('customer') ;
 
$myDb->setQuery($myQuery) ;
$MyResultsSoft = $myDb->loadColumn() ;
$MyResultsSoft = implode(",",$MyResultsSoft);
 
return $MyResultsSoft ;

Dans un champ (calc field)

Ici par exemple, pour tester ligne par ligne la correspondance avec des emails d'une table d'une base distante. Sur chaque ligne une requête WHERE s'exécute sur la base distante, avec pour paramètre l'email de la ligne courante. On ajoute du HTML/CSS pour mettre les résultats de couleurs différentes (Oui en vert et Non en rouge) :

// Declaration de la variable stockant l email d origine de chaque ligne
$SpecialEmail = '{MaTableLocale1___MonEmail}';
 
// if-else de gestion des cas vides - pour eviter de declencer une requete WHERE vide
if (!empty($SpecialEmail)) {
 
// Acces a la base de donnees
$myDb = FabrikWorker::getDbo(false, YourIdConnexion);
 
// Creation requete - argument true pour nettoyer une eventuelle ancienne requete
$myQuery = $myDb->getQuery(true);
 
// Requete de recuperation de la valeur concernee en fonction de l id d'origine
$myQuery->select('email')
->from('MaTableDistante')
->where('email = ' . $myDb->quote($SpecialEmail));
$myDb->setQuery($myQuery);
$DirectoryPresent = $myDb->loadResult();
if (!empty($DirectoryPresent)) {
 
// Calcul
return '<div style="text-align: center ; font-weight : bold ; color : green ; ">Yes</div>' ;
}
 
// Si vide
else {
return '<div style="text-align: center ; font-weight : bold ; color : red ; ">No</div>';
}}
 
// Si vide
else {
return '';
}

Autre exemple, toujours dans un champ cal, mais nous récupérons cette fois plusieurs lignes de résultats, soit un tableau : 

$monId = '{pipelines___id}';
 
$myDb = FabrikWorker::getDbo(false, 5);
 
$myQuery = $myDb->getQuery(true);
 
$myQuery->select(array('link_contact, notes'))
->from('pipelines_links')
->where('pipeline_id = ' . $myDb->quote($monId));
 
$myDb->setQuery($myQuery);
$myResults = $myDb->loadObjectList();
 
$list = array();
foreach ($myResults as $row)
{
$list[] = "".$row->link_contact . " " . $row->notes . "";
}
return implode($list);

Calcul, récupération, manipulation de champs

Récupérer le nom et le prénom à partir du nom d'utilisateur Joomla

Bien souvent on souhaite récupérer l'identité complète d'un utilisateur (dans un formulaire de profil, de commande...).

Mais Joomla ne prend qu'un seul vrai nom d'utilisateur (name). Il s'agit donc de découper ce champ en 2 nouveaux champs pour le prénom et le nom (ce qui nous permettra de ne pas toucher à l'utilisateur système, l'utilisateur Joomla).

Dans un champ firstname, dans la valeur par défaut de l'élément, nous pouvons récupérer le 1er mot saisi dans le champ utilisateur name de l'utilisateur connecté, avant un espace. Ne retournera rien s'il n'y a pas d'espace (nous préférons ici conserver la valeur pour le lastname, sans faire doublon) :

$MonName = '{$my->name}' ;
 
$Espace = ' ' ;
$CherchePosEspace = strpos($MonName, $Espace) ;
$MonName2 = substr($MonName,0,$CherchePosEspace) ;
 
return $MonName2 ;

Pour le lastname (la chaîne suivante supposée du moins), il faudra prévoir l'existence ou non d'un espace dans le name, avec une condition if, afin de le conserver tel quel si le nom de l'utilisateur ne contient qu'un seul mot :

$MonName = '{$my->name}' ;
 
// Il y a au moins un espace
if(strpos($MonName, ' ') !== false){
 
$Espace = ' ' ;
$CherchePosEspace = strpos($MonName, $Espace) + 1 ;
$MonName2 = substr($MonName,$CherchePosEspace,strlen($MonName)) ;
}
 
// Il n'y a aucun espace
else{
$MonName2 = $MonName;
}
 
return $MonName2 ;

Allié ensuite à des jointures ou d'autres règles de validation PHP ou notempty, vous pourrez contraindre la saisie de l'identité complète dans les profils, la correspondance entre les champs... ou au contraire autoriser les saisies d'objets à d'autres noms mais liés au même utilisateur.

Récupérer des données JSON

Certains champs Joomla (ou d'extensions, ou autres...) stockent des données en JSON. Pour customiser certains affichages avec Fabrik, il est intéressant de savoir récupérer les données souhaitées.

Ici par exemple, nous récupérons le code vidéo YouTube d'une URL stockée dans le champ attribs de la table #_content, juste avant de l'afficher dans un iframe.

Dans un champ calc :

$My_Json_String = '{#_content___attribs}' ;
 
// Fix quotes
$json = str_replace('&amp;quot;', '"', $My_Json_String);
$object = json_decode($json);
$videoUrl = $object->video;
 
// Recover only the Youtube Video Code (deleting the beginning of the url - to do an iframe after)
$videoCode = str_replace('https://youtu.be/', '', $videoUrl);
 
// Tests:
// var_dump($videoUrl); exit;
// echo '<pre>'. $videoCode . '</pre>';
 
// Iframe
return '<iframe width="100%" height="250px" src="https://www.youtube.com/embed/'.$videoCode.'" frameborder="0" allow="encrypted-media" allowfullscreen></iframe>' ;

Nous commençons par récupérer le champ JSON voulu dans une variable ($My_Json_String). Par commodité nous plaçons de vrais guillemets dans le JSON (str_replace), peut renvoyer NULL sans cela).

Ensuite nous décodons le JSON (json_decode) afin d'extraire la chaîne voulue (utilisez l'echo pour vérifier sa forme, ici l'URL de la vidéo).

Dans notre cas nous souhaitons intégrer la vidéo dans un iframe, nous supprimons donc le début de l'URL afin de ne conserver que le code YouTube d'intégration (str_replace).

Le return construit ensuite l'iframe dynamiquement.

Attention : Parfois, si vous n'êtes pas déjà en UTF-8, vous devrez ré-encoder votre variable avant de décoder le JSON (utf8_encode).

Un autre exemple de génération puis récupération de données JSON externes (à partir de l'API Google Maps) se trouve ici !

Calcul ou récupération de champs de tables externes

Parfois vous souhaitez utiliser des valeurs de tables jointes via un champ de type databasejoin. Prendre le prix unitaire d'un produit pour calculer le prix total d'une commande par exemple.

Votre champ databasejoin stocke typiquement l'id de l'enregistrement distant, l'utiliser dans un champ calc vous permettra de récupérer d'autres valeurs de cet enregistrement distant.

Ici nous allons récupérer un entier dans une variable nommée $MaValeurRecupere, avant de le multiplier par une donnée saisie.

// Declaration de la variable stockant l id d'origine
$MonIdDistant = '{Ma_Table___Id_Distant_raw}';
 
// if-else de gestion des cas vides - pour eviter de declencer une requete WHERE vide
if (!empty($MonIdDistant)) {
 
// Acces a la base de donnees
$myDb = JFactory::getDbo();
 
// Creation requete - argument true pour nettoyer une eventuelle ancienne requete
$myQuery = $myDb->getQuery(true);
 
// Requete de recuperation de la valeur concernee en fonction de l id d'origine
$myQuery->select('Champ_A_Recuperer')->from('Table_Distante')->where('Champ_Id = ' . $myDb->quote($MonIdDistant));
$myDb->setQuery($myQuery);
$MaValeurRecupere = $myDb->loadResult();
 
// Calcul
return $MaValeurRecupere * '{Ma_Table___Champ_Nombre}' ;
}
 
// Si vide
else {
return '';
}

Ce type de code peut tout-à-fait être utilisé dans les pré-filtres d'une liste (choisir Eval dans le champ Type d'un pré-filtre, pour interprêter du code PHP). Autrement dit, on peut lier des données provenant de bases distantes pour faire un peu plus que du simple affichage !

Sommer les valeurs d'un sous-formulaire

À partir de l'exemple précédent des commandes clients, on souhaite parfois regrouper des commandes de produits différents dans une seule commande, afin d'additionner les totaux et de produire une somme finale. On a donc un formulaire principal de commande, et ses sous-formulaires des quantités par produits.

En reprenant le code de l'exemple précédent, dans un champ calc, ou encore dans le plugin de formulaire pour Paypal, ajouter un sum dans votre requête $myQuery fera l'affaire :

$myQuery->select('sum(Champ_A_Sommer)')->from('Ma_Table')->where('Champ_Id = ' . $myDb->quote($tableId));

Utiliser des données tiers comme des variables de session

Parfois vous avez besoin de récupérer des données, internes ou externes (la BDD d'origine ou une autre) en tant que variable de session. Exemple : des données de profil utilisateur (détails, adresses, téléphones...) séparées de la table #_users. Bien sûr vous n'y avez pas accès via les variables de sessions classiques.

Si ces données sont correctement liées aux utilisateurs (un champ user_id faisant le lien avec la table #_users), vous pouvez alors les appeler une par une dans des champs calc, en utilisant {$my->id} :

// DB access, with Fabrik connection ID
$myDb = FabrikWorker::getDbo(false, 1) ;
 
// Recover query
$myQuery = $myDb->getQuery(true) ;
$myQuery->select('lastname')
->from('profiles')
->where('user_id = {$my->id}') ;
 
$myDb->setQuery($myQuery) ;
$MyData = $myDb->loadResult() ;
 
return $MyData ;

Afficher des données d'un tableau

Si vos données proviennent d'une requête SQL avec des résultats multiples par exemple :

$VideoId = '{videos___id}' ;
$Title = '{videos___title}' ;
$Speaker = '{videos___speaker_name}' ;
$Tags = '{videos___video_tags}' ;
 
// Acces a la base de donnees
$myDb = FabrikWorker::getDbo(false, 1);
 
// Creation requete - argument true pour nettoyer une eventuelle ancienne requete
$myQuery = $myDb->getQuery(true);
 
$myQuery = "
SELECT id, title, category,
CONCAT('published on ', DATE_FORMAT(date_publish, '%d/%m/%Y')) as my_date_publish,
speaker_name,
tag_unique
FROM videos_tags_uniques
WHERE tag_unique IN ('Periorbital')
AND id <> $VideoId
ORDER BY date_publish DESC
LIMIT 0,10
" ;
 
$myDb->setQuery($myQuery);
$MyResults = $myDb->loadObjectList();
 
$list = array();
foreach ($MyResults as $row)
{
$list[] = "<div><a href='/video-v3/details/42/". $row->id ."' style='font-size: 18px; font-weight: bold'>" . $row->title . "</a><font style='font-size: 14px; font-style: italic;'> - ". $row->speaker_name ." " . $row->category . ", " . $row->my_date_publish . "</font></div>";
}
 
if (!empty($MyResults)) {
return "<div style='font-size: 17px;margin-bottom: 5px;'>Others videos related</div>
<div>" . implode($list) . "</div>";
}else {
return '';
}
;

Personnaliser les listes déroulantes

Modifier les labels à la volée

Dans les plugins databasejoin ou dropdown l'onglet Advanced permet d'accéder aux valeurs (value) et aux textes (text) de la liste affichée, cela en utilisant la variable $opt (pour option).

Ainsi on peut corriger certaines des options affichées sans toucher aux valeurs réelles de la base de données. Pratique quand la base qui fournit la liste utilise des caractères un peu spéciaux, qu'on ne peut pas traiter correctement (en cas de multi-sites par exemple).

if ($opt->text == "Company&amp;#039;s Medical Staff"):
    $opt->text = "Company's Medical Staff" ;
 
elseif ($opt->text == "Ma&amp;icirc;tre d'H&amp;ocirc;tel/Head Waiter"):
    $opt->text = "Maître d'Hôtel/Head Waiter" ;
endif;

Trier les valeurs d'une liste déroulante selon l'utilisateur connecté

Le plugin databasejoin est bien pratique. Et bien il peut l'être encore plus ! Par exemple dans l'option Data - Where, vous pouvez restreindre les enregistrements proposés à ceux créés par un unique utilisateur (celui connecté) :

WHERE user_submit = {$my->id}

Imaginez alors que vous souhaitiez aider l'utilisateur dans sa saisie, en pré-remplissant le formulaire avec l'éventuel unique enregistrement saisi par cet utilisateur.

En effet si celui-ci n'a qu'une seule entrée dans sa liste, autant la lui proposer par défaut :

$db = FabrikWorker::getDbo(false, 1) ;
$db->setQuery('SELECT id FROM scientific_societies WHERE user_submit = {$my->id}') ;
$rows = $db->loadObjectList() ;
 
if (intval(count($rows)) == 1 ) {
foreach ($rows as $key => $value) {
return $value->id;
}
}

Voir aussi la rubrique Divers pour pré-remplir des champs, dont des listes déroulantes, en fonction de là d'où l'on vient (le formulaire précédent).

Trier les valeurs d'une liste déroulante selon plusieurs tests

Un autre exemple plus complexe où nous effectuons différents tests avant de choisir quel liste déroulante afficher :

Avec le plugin dropdown cette fois, pour plus de liberté. Nous affichons des profils humains (une table customer). Pour chaque fiche nous souhaitons rendre disponibles à la saisie certains métiers SI l'enregistrement questionné appartient à une catégorie de métiers (information stockée dans la table customer, champ degreeET SI l'enregistrement est lié à un organisme distinct (information stockée dans une autre table customer_organisme, champ id_organisme).

Les métiers devant peupler la liste déroulante sont eux-mêmes dans une 3ème table specialities, avec un champ permettant de distinguer 3 listes de métiers (role).

Nous commençons par faire deux tests basés sur le résultat d'une requête avec jointure (afin de ramener les information des 2 tables ici impliquées dans les profils). Ensuite nous créons les 3 listes déroulantes distinctes. Enfin, nous affichons la liste voulue en fonction des résultats des tests.

Attention suivez bien ! Dans l'onglet Advanced/Eval populate du plugin dropdown :

// Déclaration de l'id de l'enregistrement à tester
$my_id = '{customer___id_customer}';
 
// Test 1 de l'existence de l'enregistrement dans les id à distinguer
$db_test_organisme_vcs_md_do = FabrikWorker::getDbo(false, 5) ;
$db_test_organisme_vcs_md_do->setQuery('SELECT customer.id_customer FROM customer INNER JOIN customer_organisme ON customer.id_customer = customer_organisme.id_customer AND id_organisme = 6 WHERE customer.degree IN ("MD", "DO") AND customer.id_customer = ' . $db_test_organisme_vcs_md_do->quote($my_id) ) ;
$rows_test_organisme_vcs_md_do = $db_test_organisme_vcs_md_do->loadObjectList() ;
 
// Test 2 de l'existence de l'enregistrement dans les id à distinguer
$db_test_organisme_vcs_nonmd = FabrikWorker::getDbo(false, 5) ;
$db_test_organisme_vcs_nonmd->setQuery('SELECT customer.id_customer FROM customer INNER JOIN customer_organisme ON customer.id_customer = customer_organisme.id_customer AND id_organisme = 6 WHERE customer.degree LIKE "Non-MD" AND customer.id_customer = ' . $db_test_organisme_vcs_nonmd->quote($my_id) ) ;
$rows_test_organisme_vcs_nonmd = $db_test_organisme_vcs_nonmd->loadObjectList() ;
 
// Test 3 de l'existence de l'enregistrement dans les id à distinguer
$db_test_organisme_vcs_vide = FabrikWorker::getDbo(false, 5) ;
$db_test_organisme_vcs_vide->setQuery('SELECT customer.id_customer FROM customer INNER JOIN customer_organisme ON customer.id_customer = customer_organisme.id_customer AND id_organisme = 6 WHERE customer.degree LIKE "" AND customer.id_customer = ' . $db_test_organisme_vcs_nonmd->quote($my_id) ) ;
$rows_test_organisme_vcs_vide = $db_test_organisme_vcs_vide->loadObjectList() ;
 
// Création de la liste déroulante en cas de tests 1, 2 et 3 négatifs
$db_emc = FabrikWorker::getDbo(false, 5) ;
$db_emc->setQuery('SELECT distinct speciality FROM specialities WHERE role = 1') ;
$rows_emc = $db_emc->loadObjectList() ;
 
// Création d'une autre liste déroulante restreinte si le test 1 est positif
$db_vcs1 = FabrikWorker::getDbo(false, 5) ;
$db_vcs1->setQuery('SELECT id_specialities, speciality FROM specialities WHERE role = 2') ;
$rows_vcs1 = $db_vcs1->loadObjectList() ;
 
// Création d'une autre liste déroulante restreinte si le test 2 est positif
$db_vcs2 = FabrikWorker::getDbo(false, 5) ;
$db_vcs2->setQuery('SELECT id_specialities, speciality FROM specialities WHERE role = 3') ;
$rows_vcs2 = $db_vcs2->loadObjectList() ;
 
// Création d'une autre liste déroulante restreinte si le test 3 est positif
$db_vcs3 = FabrikWorker::getDbo(false, 5) ;
$db_vcs3->setQuery('SELECT id_specialities, speciality FROM specialities WHERE role IN (2,3,4)') ;
$rows_vcs3 = $db_vcs3->loadObjectList() ;
 
// Affichage de la liste déroulante restreinte en cas de test 1 positif
if (!empty($rows_test_organisme_vcs_md_do)) {
foreach ($rows_vcs1 as $row) {
$options_vcs1[] = JHTML::_('select.option', $row->speciality, $row->speciality) ;
}
return $options_vcs1 ;
}
 
// Affichage de la liste déroulante restreinte en cas de test 2 positif
if (!empty($rows_test_organisme_vcs_nonmd)) {
foreach ($rows_vcs2 as $row) {
$options_vcs2[] = JHTML::_('select.option', $row->speciality, $row->speciality) ;
}
return $options_vcs2 ;
}
 
// Affichage de la liste déroulante restreinte en cas de test 3 positif
if (!empty($rows_test_organisme_vcs_vide)) {
foreach ($rows_vcs3 as $row) {
$options_vcs3[] = JHTML::_('select.option', $row->speciality, $row->speciality) ;
}
return $options_vcs3 ;
}
 
// Affichage de la liste déroulante en cas de test négatif
else {
foreach ($rows_emc as $row) {
$options_emc[] = JHTML::_('select.option', $row->speciality, $row->speciality) ;
}
return $options_emc ;
}

Icônes dynamiques

on peut déjà grâce à Fabrik, remplacer statiquement des valeurs de champs par des icônes (dans les Paramètres de la vue liste d'un élément). En utilisant des valeurs de champs contrôlés en alias d'images, on peut même remplacer dynamiquement ces valeurs.

Mais nous allons faire ici quelque chose d'encore plus sympa !

Nous regardons si le contenu d'un champ Email se trouve également dans 3 autres champs Email d'une table d'une base externe, puis faisons apparaître le résultat sous forme d'une belle icône d'alerte, avec popup d'info textuelle. Cela dans un champ Calc :

// Champ a chercher
$EmailRef = '{ma_table___mon_champ_email}' ;
 
// if-else de gestion des cas vides - pour eviter de declencer une requete WHERE vide
if (!empty($EmailRef)) {
 
// Acces a la connexion distante
$myDb = FabrikWorker::getDbo(false, ID_De_Ma_Connexion_Externe);
 
// Creation requete - argument true pour nettoyer une eventuelle ancienne requete
$myQuery = $myDb->getQuery(true);
 
// Requete de recuperation de la valeur concernee
$myQuery->select('
Ma_table_Distante.Mon_Champ_Id
from Ma_table_Distante
where email = ' . $myDb->quote($EmailRef) .
'or email2 = ' . $myDb->quote($EmailRef) .
'or email3 = ' . $myDb->quote($EmailRef)
);
 
$myDb->setQuery($myQuery);
$MyTest = $myDb->loadResult();
 
if (!empty($MyTest)) {
 
// Calcul
return 'Email existant en base distante !' ;
}
 
// Si pas de resultat
else {
return '';}
}
 
// Si le champ a chercher est vide
else {
return '';
}

Nous ne faisons que tester l'existence de résultats, avant d'afficher un texte en dur si un enregistrement est renvoyé. La requête ne renvoie donc que les ID, car plus légers, mais ils ne seront pas utilisés par la page.

Notez que nous pourrions aussi, juste après le test en cas de retour positif, lancer une recherche sur la liste externe à partir du champ à tester (voir la section sur les recherches dynamiques).


Calculs sur des champs date

Extraire une partie d'une date

Bien pratique pour faire des regroupements dynamiques sur les mois par exemple, dans un calendrier ou une application de réservation.

Utilisez les constantes de date PHP, ici F (mois textuel complet) et Y (année complète en chiifre). Ici nous les séparons par une virgule, et les stockons dans une variable nommée $FullMonthAndYear.

Dans un champ calc :

$FullMonthAndYear = '{table___date_field_raw}' ;
 
$FullMonthAndYear = date("F, Y", strtotime($FullMonthAndYear)) ;
 
return $FullMonthAndYear ;

Filtrer une liste sur la date actuelle

Dans un Pre-filter classique :

DATE(NOW())

Comparer une date avec une autre

Pour vérifier qu'une date saisie est bien supérieure/inférieure/égale à une autre. Dans le cas de systèmes de réservation par exemple, vous aurez sans doute par la suite des process nécessitant des champs dates correctement saisis (créer un calendrier, chercher les overlaps...).

Dans le code ci-dessous, les dates sont formatées en AnnéeMoisJour (ex : 20161208), nous ne comparons donc ici que les jours, un jour de fin devant être supérieur au jour de début par exemple.

Dans une règle de validation PHP (sur un élément), ajoutez simplement ce type de code, qui doit retourner true pour valider le formulaire :

// date 1
$startDate = $formModel->formData['ma_table___date_1'];
$startDate = new DateTime($startDate);
$startDate = $startDate->format('Ymd');
 
// date 2
$endDate = $formModel->formData['ma_table___date_2'];
$endDate = new DateTime($endDate);
$endDate = $endDate->format('Ymd');
 
//var_dump ($startDate,$endDate) ;
 
return $endDate > $startDate ;

Compter les nuits entre 2 champs date

Bien pratique pour un système de réservation de chambres par exemple. Dans un champ calc (de type text) :

// Suppression des avertissements PHP en cas d'absence de timezone dans le INI
if( ! ini_get('date.timezone') )
{date_default_timezone_set('GMT');}
 
// Déclaration du champ date de début et vidange du settime
$datetime1 = new DateTime('{ma_table___mon_champ_date1}');
$datetime1->setTime(0,0,0);
 
// Déclaration du champ date de fin et vidange du settime
$datetime2 = new DateTime('{ma_table___mon_champ_date2}');
$datetime2->setTime(0,0,0);
 
// Calcul de l'intervalle via la fonction diff, et formattage en jours (%a), équivalent en nuits puisque simple intervalle
$interval = $datetime1->diff($datetime2);
 
return $interval->format('%a');

Retrancher automatiquement un jour

Parfois vous avez besoin de retrancher un jour du jour de fin d'un événement. Dans le cas de réservations de chambres d'hôtel par exemple, les nuits vont être réservées mais les jours de départ d'un client doivent être affichés comme disponibles (cela rendra un calendrier mensuel plus lisible, en ne comptabilisant pas les jours de départ).

Ce code fonctionne dans un champ calc (de type text), qui va reprendre le champ datetime à retrancher :

$MaDateOriginale = new DateTime('{ma_table___mon_champ_date}');
return $MaDateOriginale->modify('-1 day')->format('Y-m-d H:i:s');

Trouver des chevauchements de périodes

Pour divers systèmes de réservation, on souhaite trouver des chevauchements (overlap) de périodes entre 2 champs dates pour un même objet. Au moment de saisir une réservation typiquement, pour vérifier que l'objet concerné n'est pas déjà booké.

Le code suivant commence par récupérer 4 variables du formulaire courant :

  1. l'id de la réservation (nous avons donc une table des réservations)
  2. l'id de l'objet (un outil, une chambre... les objets ont leur propre table, dont nous récupérons l'id dans la table des réservations)
  3. la date de début de la réservation
  4. la date de fin de la réservation

La requête cherche ensuite les réservations existantes dont les dates de début sont inférieures à la date de fin saisie et dont les dates de fin sont supérieures à la date de début saisie, cela uniquement pour un objet (inclusion dans la requête de l'id de l'objet) et sans compter la réservation courante (exclusion de l'id de la réservation).

Remarquez également dans le code, la dernière condition de la requête, qui ne s'exécute que si la variable n'est pas vide. Ceci afin de ne pas faire échouer la requête dans certains cas (mais aucune différence ici). Defensive coding dirait Cheesegrits (Fabrik).

Les variables en question sont ici dans un sous-formulaire répétable, donc dans un tableau (array). Si ce n'est pas votre cas, vous n'avez qu'à supprimer les [$repeatCounter] ainsi que les lignes contenant is_array.

Cela dans une règle de validation PHP d'un élément (celui de l'objet de la réservation idéalement), qui va retourner true si aucun overlap n'est trouvé, sinon false, qui fera échouer la validation du formulaire.

// Get the booking_room id, might be an array - Useful to exclude the current record of the request, in order to authorize the modification of this record
$myBookingRoomId = $formModel->formData['table_reservation___id'][$repeatCounter];
$myBookingRoomId = is_array($myBookingRoomId) ? $myBookingRoomId[0] : $myBookingRoomId;
 
// Get the room id from the booking_room table, might be an array
$myRoomId = $formModel->formData['table_reservation___room_raw'][$repeatCounter];
$myRoomId = is_array($myRoomId) ? $myRoomId[0] : $myRoomId;
 
// Date 1, might be an array
$startDate = $formModel->formData['table_reservation___date_1'][$repeatCounter];
$startDate = is_array($startDate) ? $startDate[0] : $startDate;
$startDate = new DateTime($startDate);
$startDate = $startDate->format('Ymd');
 
// Date 2, might be an array
$endDate = $formModel->formData['table_reservation___date_2'][$repeatCounter];
$endDate = is_array($endDate) ? $endDate[0] : $endDate;
$endDate = new DateTime($endDate);
$endDate = $endDate->format('Ymd');
 
//var_dump($endDate);
 
// Now do the query ...
$myDb = FabrikWorker::getDbo();
$myQuery = $myDb->getQuery(true);
 
$myQuery
->select('*')
->from('app_booking_rooms')
->where("DATE_FORMAT(table_reservation.date_1, '%Y%m%d') <= '$endDate' -1")
->where("DATE_FORMAT(table_reservation.date_2, '%Y%m%d') -1 >= '$startDate'")
->where("table_reservation.room = '$myRoomId'");
 
if (!empty($myBookingRoomId)) {
$myQuery->where("table_reservation.id <> '$myBookingRoomId'");
}
 
// Uncomment this to test, make sure the query is sane
 
//var_dump((string) $myQuery);
 
//var_dump ($startDate,$endDate) ;
 
$myDb->setQuery($myQuery);
$myResults = $myDb->loadObjectList();
 
// Return false if we found any rows
return empty($myResults);

Ce code fomate les dates pour récupérer une concaténation numérique de l'année complète, du mois et du jour (ex : 20161208).

Cela facilite les comparaisons numériques et permet si besoin de faire des opérations sur ces valeurs (nous retranchons ici un jour des dates de fin, le -1 dans la requête).

Modifiez le code si vous souhaitez utiliser les horaires complètes, mais attention aux fuseaux horaires, je crois que dans ce cas une conversion en GMT sera préférable (CONVERT_TZ).


API Google Map

Lancer la navigation Google Map depuis un formulaire à partir des coordonnées géographiques

Dans un Rendu Fabrik Google Map, on peut déjà, à partir d'un popup d'un point de la carte, lancer la navigation Google Map vers ce point (ou un autre). Ceci grâce à la balise {nav_url}.

Mais en recréant le lien dynamiquement, on peut lancer la navigation à partir d'un formulaire. D'abord un exemple d'une URL dure, lançant la navigation sur Google Map :

http://maps.google.com/maps?q=loc:49.038684733966846,2.0750216000001274&navigate=yes

Il s'agit donc de créer soi-même ce lien, et dynamiquement, pour insérer les coordonnées des enregistrements.

Mais avant cela il faut nettoyer le champ contenant les coordonnées, car il contient des parenthèses et le zoom par défaut (sous forme (lat,lon):4). Ce nettoyage est possible en écrivant son propre PHP, mais inutile de se fatiguer, c'est déjà prévu par PHP (ou Joomla/Fabrik, je ne sais pas). Ceci avec la fonction mapStrToCoords.

Le code suivant, dans un champ Calc va donc créer des boutons lançant la navigation à partir d'un formulaire vers des champs de type Élément Google Map :

// get the simple "lat,long" strings
$coords = FabrikString::mapStrToCoords('{MaTable___Champ_GM_Depart}') ;
$startCoords= $coords->lat . ',' . $coords->long;
 
$coords = FabrikString::mapStrToCoords('{MaTable___Champ_GM_Destination}') ;
$endCoords = $coords->lat . ',' . $coords->long;
 
//build the URL
$NavVersDepart = "http://maps.google.com/maps?q=loc:$startCoords&amp;navigate=yes" ;
$NavVersDestination = "http://maps.google.com/maps?q=loc:$endCoords&amp;navigate=yes" ;
 
return '<div style="line-height: 30px;"><a style="margin:20px" href='.$NavVersDepart.' target="_blank" >Naviguer vers départ</a><a style="margin:20px" href='.$NavVersDestination.' target="_blank" >Naviguer vers destination</a><br><a href="#">Modifier</a>';

Lancer une recherche Google Map depuis des champs textuels

Autre exemple plus simple, où on souhaite créer un lien vers une recherche GM à partit de 3 champs textuels (add, city et country)

Dans un champ calc, on concatène les champs textuels pour les inclure dans un lien, et on affiche les champs à l'intérieur de ce lien :

$Add = '{table___add}' ;
$City = '{table___city}' ;
$Country = '{table___country}' ;
 
return '<div style = "" >
 
<a href="https://www.google.com/maps/search/'.$Add.' '.$City.' '.$Country.'" target="_blank">
 
<div style = "" >'.
$Add.'
</div>
 
<div style = "" >'.
$City.'
</div>
 
<div style = "">'.
$Country.'
</div>
 
</a>
 
</div>' ;

Récupérez la distance routière et le temps de trajet entre 2 points

L'API Google Map permet de récupérer la distance et le temps de trajet routier entre 2 coordonnées géographiques, cela sous forme de lien renvoyant un fichier de type JSON :

https://maps.googleapis.com/maps/api/distancematrix/json?origins=49.037463,%202.003490100000022&destinations=49.0449034,%202.0419157999999697

Si votre table possède 2 éléments Google Map, vous pouvez les utiliser pour créer dynamiquement ces liens afin de récupérer les valeurs, ou simplement les afficher.

Cela depuis un plugin PHP sur un formulaire, sous cette forme :

// get the simple "lat,long" strings
$coords = FabrikString::mapStrToCoords('{booking___start_address_coord}') ;
$startCoords= $coords->lat . ',' . $coords->long;
$coords = FabrikString::mapStrToCoords('{booking___end_address_coord}') ;
$endCoords = $coords->lat . ',' . $coords->long;
 
//build the URL, with API key
$distanceURL = "https://maps.googleapis.com/maps/api/distancematrix/json?origins=$startCoords&amp;destinations=$endCoords&amp;key=YOUR_KEY" ;
 
// get the result and decode it
$distanceData = file_get_contents($distanceURL);
$distanceResult = json_decode($distanceData);
 
$DistanceMeter = $distanceResult->rows[0]->elements[0]->distance->value ;
$DistanceTime = $distanceResult->rows[0]->elements[0]->duration->text ;
 
// Calculate kilometers
$DistanceKilometer = round($DistanceMeter/1000,1) ;
 
// Set results in dedicated fields
if ($distanceResult->status === 'OK') {
$formModel->updateFormData('MY_TABLE___distance_m', $DistanceMeter, true);
$formModel->updateFormData('MY_TABLE___time_min', $DistanceTime, true);
$formModel->updateFormData('MY_TABLE___distance_km', $DistanceKilometer, true);
}

L'API nous renvoie la distance en mètres ainsi qu'une chaîne de texte mentionnant les kilomètres. Nous la recalculons ici pour obtenir les kilomètres sous forme numérique, plus pratique pour calculer le prix d'une course d'après un forfait kilométrique par exemple. Vous disposez aussi du temps en minutes et en secondes.

Le dernier paragraphe vient remplir 3 champs dédiés préalablement créés, avec les résultats.

Plus d'info sur l'API Distance MatrixDistance Matrix

API étudiants : AIzaSyBDxMhC_Oj4tXgGifJV77sjFiR3EE89KA4

Ouvrir un popup par défaut

Pour déclencher l'ouverture par défaut du popup d'informations d'un enregistrement, il faut simuler un clic sur l'icône de cet enregistrement. Le code suivant, dans un fichier javascript (.js) situé dans components/com_fabrik/js et nommé viz_ID_DE_VOTRE_CARTE.js, ouvre le popup du 1er enregistrement affiché sur une carte ([0]). Modifiez le nombre pour atteindre l'enregistrement voulu :

requirejs(['fab/fabrik'], function () {
Fabrik.addEvent('fabrik.viz.googlemap.ajax.refresh', function (viz) {
google.maps.event.trigger(viz.markers[0], 'click');
});
});

API Vimeo

Vimeo fournit les informations publiques de configuration des vidéos dans des tableaux disponibles par URL. Exemple :

https://player.vimeo.com/video/274514368/config

Le chiffre étant l'id Vimeo de la vidéo. Vous pouvez alors décoder ce tableau, ou récupérer certaines informations par extraction.

Ici un exemple bricolage de récupération de la durée de la vidéo (duration) dans un champ calc. Je ferai mieux plus tard, en parsant les données de l'URL :

$IdVimeo = '{videos___vimeo_id}' ;
 
$VideoConf = 'https://player.vimeo.com/video/'.$IdVimeo.'/config' ;
 
$VideoConfChar = file_get_contents($VideoConf);
$FindDuration = '"duration":' ;
$FindThumbs = ',"thumbs":' ;
 
$PositionDuration = stripos($VideoConfChar, $FindDuration);
$PositionThumbs = stripos($VideoConfChar, $FindThumbs);
 
$DurationSeconde = substr($VideoConfChar, $PositionDuration+11, ($PositionThumbs-11)-$PositionDuration);
 
$DurationMinute = gmdate("i:s", $DurationSeconde) ;
 
return $DurationMinute ;

Export PDF

Ajouter une image de fond dans un export PDF

Dans un template dédié placé ici :

components \ com_fabrik \ views \ details \ tmpl \ MON_TEMPLATE \ custom_css.php

Ajoutez ce code par exemple :

body {
background-image: url(/Mon_IMAGE.jpg);
background-repeat: no-repeat;
background-position:center center;
background-attachment: fixed;
text-align : center ;
margin-top: -5px !important ;
margin-bottom: 1px !important ;
padding: 0px !important ;
}

Avec l'image MON_IMAGE.jpg placée à la racine du template de la vue.

Customiser dynamiquement le nom d'un export PDF

Créez un override dans le template dédié de votre vue détail, ici :

components \ com_fabrik \ views \ details \ tmpl \ MON_TEMPLATE \ layouts \ form \ fabrik-pdf-title.php

Puis utilisez ce code par exemple, pour modifier dynamiquement (avec des données) le nom du fichier exporté :

<?php
defined('JPATH_BASE') or die;
$d = $displayData;
echo FArrayHelper::getValue($d->model->data, 'MaTable___MonChamp1', 'TexteSiVide') . ' ' . FArrayHelper::getValue($d->model->data, 'MaTable___MonChamp2', 'TexteSiVide') . ' - AutreTexte' ;

Ceci marche de façon similaire pour les listes.

Plus de documentation ici : http://fabrikar.com/forums/index.php?wiki/jlayouts/#form-layouts


Javascript

Ajouter du code Javascript dans un formulaire

Il existe plusieurs endroits où jouer du Javascript, mais pour associer un code Javascript à tout un formulaire, adoptez la méthode suivante :

Créez un fichier form_XX.js dans components / com_fabrik / js, où XX est l'id du formulaire, puis insérez vos fonctions.

Ensuite, appelez simplement les fonctions à partir de vos actions JS d'élément.

Plus d'info ici !


Construire un formulaire de souscription pour Acymailing avec Fabrik

Pour plusieurs raisons, on souhaite parfois personnaliser au maximum le formulaire de souscription d'un outil newsletter. Ici un exemple simplifié avec l'extension Acymailing.

À noter qu'une documentation Acymailing existe permettant d'utiliser des liens de souscription à intégrer dans Fabrik (ICI). Mais ce n'est pas ainsi que nous procéderons ci-dessous, où nous préfèrererons créer nos propres checkboxs pour gérer les souscriptions.

Avant toute chose, appelez votre table #_acymailing_subscriber dans Fabrik, et interdisez la modification des types de champs (dans les paramètres de la liste, Advanced).

Commencez ensuite la construction de votre formulaire classiquement. Enfin, adaptez les codes suivants :

Concaténation du champ name

Parfois vous souhaitez afficher des champs de type lastname et firstname. Mais c'est le champ name d'Acymailing qui sera utilisé dans les newsletters. Gérez-le comme voulu (concaténation, casse...).

Faîtes en un champ calc, caché, puis par exemple :

return '{#_acymailing_subscriber___firstname}'.' '.'{#_acymailing_subscriber___lastname}' ;

Gestion des champs date en timestamp Unix

En effet Acymailing utilise le format de date Unix timestamp. Or Fabrik n'implémente pas nativement ce format. Il faudra donc le gérer en PHP et SQL.

Dans la table #_acymailing_subscriber, dans le champ created, caché, en Default value et Eval mode :

return time() ;

Le champ date de la table #_acymailing_listsub sera rempli dans le plugin de formulaire suivant, via la fonction UNIX_TIMESTAMP().

Inscription à des listes liées à un bouton checkbox

Ici un exemple où nous ne permettons le choix qu'entre 2 listes, facilement adaptable à vos besoins.

Il va falloir créer un champ Fabrik de type checkbox, qui va prendre en compte le choix de l'utilisateur. Ici ce champ est appelé lists, et pour une meilleure compéhension, nous utiliserons ses labels dans le code ci-dessous, et nous ajoutons les id respectifs dans les labels (My list ID 3, etc...).

Ce champ lists sera nécessaire uniquement pour la 1ère souscription de l'abonné. En effet pendant la soumission du formulaire, le code va répercuter les choix de l'utilisateur dans la table #_acymailing_listsub (et là par contre, ce sont les id des listes concernées qui sont utilisés dans le code, vous devez donc les connaitre et les adapter dans les requêtes SQL du code).

Ce code est fait pour un champ lists placé dans la table #_acymailing_subscriber (plus simple à faire et à expliquer), mais des commentaires permettent de l'adapter pour utiliser une table jointe (ce qui sera bien plus orienté base de données relationnelles, conseillé !).

Les 2 premières conditions elseif gèrent les cas où l'utilisateur a choisi une seule des listes disponibles à la saisie. Le dernier elseif celui où l'utilisateur choisit les 2 listes.

Dans un formulaire pointant en table principale sur #_acymailing_subscriber, avec un champ subid en tant qu'internal id, dans un plugin de formulaire donc, en mode End of form submission :

// Recover subid created
$My_Subid = $formModel->formData['#_acymailing_subscriber___subid'] ;
 
// Recover lists choosen in chekbox field
$My_List = $data['#_acymailing_subscriber___lists'] ;
// Implode if data comes from an array (joined table), comment/uncomment if necessary
// $My_List = implode(',', $My_List);
 
if ($My_List === "My list ID 3")
{
//Query 1
defined('_JEXEC') or die('Restricted access');
$db = FabrikWorker::getDBO(false, 1);
$query = "
INSERT INTO #_acymailing_listsub (subid, listid, status, subdate)
VALUES ('$My_Subid', '3', '1', UNIX_TIMESTAMP())
" ;
$db->setQuery($query);
$db->query();
}
 
elseif ($My_List === "My list ID 6")
{
//Query 2
defined('_JEXEC') or die('Restricted access');
$db = FabrikWorker::getDBO(false, 1);
$query = "
INSERT INTO #_acymailing_listsub (subid, listid, status, subdate)
VALUES ('$My_Subid', '6', '1', UNIX_TIMESTAMP())
" ;
$db->setQuery($query);
$db->query();
}
 
// Last elseif if data does not come from an array
elseif ($My_List === "<ul><li>My list ID 3</li><li>My list ID 6</li></ul>")
 
// Or if data comes from an array
// elseif ($My_List === "My list ID 3,My list ID 6")
{
//Query 3
defined('_JEXEC') or die('Restricted access');
$db = FabrikWorker::getDBO(false, 1);
$query = "
INSERT INTO #_acymailing_listsub (subid, listid, status, subdate)
VALUES ('$My_Subid', '3', '1', UNIX_TIMESTAMP()), ('$My_Subid', '6', '1', UNIX_TIMESTAMP())
" ;
$db->setQuery($query);
$db->query();
};
 
// var_dump($My_List);
// exit;

Champ key

Attention : le 12/10/2018 le support Acymailing me confirme que la gestion de ce champ est inutile au moment de la création d'un nouvel abonné. En effet ce champ sera automatiquement généré au moment de l'envoi d'un 1er email. Je laisse toutefois le code ci-dessous en cas de changement ou si besoin.

Le champ key est indispensable pour le bon fonctionnement d'Acymailing (les désinscriptions et views on line notamment, les balises, etc...), il doit contenir une chaîne randomisée unique de 14 caractères (du moins unique pour tout humain unique, de ce que je comprends, car j'ai observé des mises-à-jour étonnantes sur ce champ, où Acy reliait des adresses emails à la même personne, et correctement ! Bref).

Dans un champ classique, caché bien sûr, en Default value et Eval mode. Modifiez uniquement le nom de la table :

$myDb = FabrikWorker::getDbo(false, 1);
$myQuery = $myDb->getQuery(true);
$oops = 0;
 
do {
// these 3 lines are just here to prevent locking up your web server in an infinite loop doing database lookups
// if I made a mistake in the code, you can remove them once it's working
if ($oops++ > 10) {
return "Something went wrong!";
}
$myString = substr(str_shuffle(str_repeat('0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ', mt_rand(1,14))),1,14);
// replace 'yourfield' with this element's name, and 'yourtable' with this form's table name
$myQuery->select('COUNT(*) AS foo')
->from('#_acymailing_subscriber')
->where('#_acymailing_subscriber.key = ' . $myDb->quote($myString));
$myDb->setQuery($myQuery);
//var_dump((string)$myQuery);exit;
try {
$myResult = $myDb->loadResult();
}
catch (Exception $e)
{
//$this->app->enqueueMessage($e->getMessage());
return 'Something went wrong!';
}
} while (!empty($myResult));
 
return $myString;

Débogage

Liste ou formulaire incorrects

Parfois une liste ou un formulaire ne fonctionne pas, et/ou affiche une erreur 500 du type Fabrik has generated an incorrect query for the list. Vous pouvez alors utiliser le débogage et obtenir un message d'erreur SQL plus explicite.

Il faut d'abord l'activer dans Options/Debugging/Allow fabrikDebug. Ensuite ajouter ?fabrikdebug=1 à la fin de l'URL de votre page en défaut. Si vous n'utilisez pas les URL SEF (Search Engine Friendly, dans les Paramètres SEO de la Configuration), utilisez plutôt la forme &fabrikdebug=1. toujours accolée à la fin de votre URL à tester.

Voir la structure des données d'un formulaire dans un plugin de formulaire

Ajoutez ce code dans un plugin PHP de formulaire pour voir tous les tableaux d'un formulaire après soumission (Start or form submission - onBeforeProcess) :

echo " ";print_r($formModel->formData);exit;

Voir le tableau (array) d'un formulaire

Pour vérifier les données appelées dans un formulaire, utilisez ce code dans un champ calc :

echo ' ';
print_r($data);
echo ' ';
exit;

Et après avoir mis l'option fabrikDebug, vous rendre sur l'URL à tester, si besoin suffixée de ?fabrikdebug=1.

Voir la tableau (array) d'une tâche programmée via un plugin PHP (Schedule ou Cron)

Dans le script php mettez ce code, puis lancez la tâche via l'administration :

<?php
print_r($data); exit ;
?>

Notez que le tableau d'un PHP cron plugin est un tableau de tableaux d'objets (voir si besoin l'onglet Récupération de variables).

Voir une requête générée

Dans un plugin PHP de formulaire par exemple, ou un cron, si vous voulez voir la forme de la requête que vous tentez d'exécuter, commencez par commenter/désactiver l'appel de l'exécution de votre requête, qui se présente souvent sous cette forme :

$db->setQuery($myQuery);
$db->query();

Puis ajoutez à la suite :

var_dump((string) $myQuery);
exit;

Voir une variable

Dans un plugin PHP de formulaire, après avoir déclaré/créé votre variable :

var_dump($MaBelleVariable);
exit;
Liens ou pièces jointes
Accéder à cette adresse URL (http://fabrikar.com/forums/index.php?wiki/troubleshooting-tools/)Wiki Fabrik sur les troubleshooting tools[Article]0 Ko
Accéder à cette adresse URL (http://fabrikar.com/forums/index.php?wiki/calculation-element/)Wiki Fabrik sur les champs calc pour débogage[Article]0 Ko
Accéder à cette adresse URL (http://fabrikar.com/forums/index.php?wiki/php-form-plugin/)Wiki Fabrik sur le plugin PHP pour les formulaires[Article]0 Ko
Accéder à cette adresse URL (http://fabrikar.com/forums/index.php?wiki/filtering-lists-tables/)Filtres de listes[Article officiel Fabrik sur les pré-filtres de listes]0 Ko
Accéder à cette adresse URL (https://api.joomla.org/cms-3/classes/JDatabaseDriver.html)Joomla! API[Documentation Joomla]0 Ko
Accéder à cette adresse URL (https://docs.joomla.org/Selecting_data_using_JDatabase)Selecting data using JDatabase[Documentation Joomla]0 Ko