Présentation du concept d’invocation, description des invokable datasets existants, méthode de création et d’utilisation des invokable dataset et dataqueries.

Edit me

Les Invokable Datasets et Dataqueries

Le concept

L’invocation de datasets et de dataqueries a pour but d’éviter la réplication de code xml et de rendre disponibles des sources de données toutes prêtes au niveau d’un projet ou d’une application.

L’idée est d’écrire une bonne fois pour toute un dataquery ou un dataset et de pouvoir l’appeler simplement au sein d’autres ressources en appliquant l’attribut invoke_from à une balise <dataquery/> ou à une balise <dataset/>.

Le système prévoit également la possibilité de personnaliser l’invokable selon ses besoins en lui passant des noeuds supplémentaires (le détail est expliqué plus bas).

Les invokable datasets

Utilisation

Plusieurs invokables datasets sont disponibles dans le noyau Voozanoo4.

Il est également possible de créer ses propres invokables datasets.

Pour les invoquer, il suffit d’insérer une balise dans sa ressource en spécifiant un id et en ajoutant un attribut invoke_from avec le nom de l’invokable souhaité :

Syntaxe générale :

<dataset id="mon_dataset" invoke_from="UnInvokableDataset" />

Exemple :

<!-- Exemple avec l'invokable IndentedGroups disponible dans le noyau -->
<dataset id="groups" invoke_from="IndentedGroups"/>

Certains de ces invokables(un seul invokable dataset du noyau actuellement : IndentedGroupsUserObject) acceptent des paramètres :

Syntaxe générale :

<dataset id="mon_dataset" invoke_from="InvokableDatasetName">
    <params>
        <value alias="param_1_name">value</value>
        <value alias="param_2_name">value</value>
    </params>
</dataset>

Exemple :

<!-- Exemple avec l'invokable IndentedGroupsUserObject disponible dans le noyau -->
<dataset id="groups" invoke_from="IndentedGroupsUserObject">
    <params>
        <!-- Liste des groupes sans les groupes désactivés / à false par défaut, 0 pour mettre à true -->
        <value alias="exclude_disabled">0</value>
        <!-- Liste des groupes dont le "level" est inférieur ou égal à 1 / aucune limite par défaut -->
        <value alias="limit_to_level">1</value>
    </params>
</dataset>

Création

Il est également possible de créer des invokables datasets spécifiques au niveau d’une application ou au niveau d’un projet.

Invokable dataset spécifique à un projet -> Création via Epicraft

1 - Dans epicraft, créer une ressource de type xml au sein d’un projet, y décrire le dataset souhaité. Par exemple :

<!--L'id sera remplacé par l'id du dataset qui invoquera ce dataset-->
<!-- Exemple : Référentiel de spécialités médicales -->
<dataset id="xxx" mode="r">
    <metadata>
        <fields>
            <field id="id_data" type="primary_key" />
            <field id="code" type="string" default_label="Trigramme">
                <string length="3" />
            </field>
            <field id="label" type="string" default_label="Nom">
                <string length="150" />
            </field>
            <field id="desc" type="string" default_label="Description">
                <string length="300" />
            </field>
        </fields>
    </metadata>
    <rowdata>
        <row id_data="1" code="ADC" label="Addictologie" desc="Partie de la médecine qui étudie le besoin anormal d’utiliser certaines substances ou de pratiquer certaines activités et les moyens d'aider les personnes qui en souffrent." />
        <row id_data="2" code="CRD" label="Cardiologie" desc="Partie de la médecine qui étudie le cœur, les artères et les veines, leurs fonctionnements, leurs maladies et les moyens de les soigner." />
        <row id_data="3" code="DNT" label="Dentisterie" desc="Étude et pratique des soins des dents, de la bouche et des mâchoires." />
        <row id_data="4" code="HMT" label="Hématologie" desc="Partie de la médecine qui étudie le sang, la lymphe et les organes qui les fabriquent, leurs fonctionnements, leurs maladies et les moyens de les soigner." />
	<!-- etc. -->
    </rowdata>
</dataset>

2 - Récupérer l’id de la ressource dans la barre d’adresse du navigateur : https://epicraft.voozanoo.net/client/#/edit-gui/dutvgjsoot1550590800637/bjsoefzdhm1565107626594.

Cette information est également récupérable après publication en base de données - colonne resource_id dans la table {pj}_resource_data - ou dans la liste des ressources du projet au sein de l’application - colonne Resource ID / Identifiant de la ressource.

3 - Utiliser cet identifiant pour invoquer le dataset :

<dataset id="specialites_medicales" invoke_from="bjsoefzdhm1565107626594" />

Invokable dataset spécifique à une application -> Création dans le code source de l’application

Les invokables datasets reposent sur 2 fichiers :

  • Le fichier xml décrivant le dataset ou le dataquery qui remplira le dataset
  • Le fichier php en charge de récupérer la description xml, de définir les éventuels paramètres acceptés par l’invokable et de lui appliquer les modifications désirées (remplissage du dataset, filtrage ou mise en forme des données, transformation du dataquery en dataset, etc.)

Ces deux fichiers doivent être placés dans le code source de l’application, plus précisément, dans le dossier “library” > “Model” > “InvokableDataSet”.

Les étapes pour créer un invokable spécifique à une application :

1 - Si ce n’est pas déjà fait, créer les dossiers “Model” > “InvokableDataSet” dans le dossier “library” de l’application et ajouter le namespace “Model_” dans l’application.ini :

; équivalent à un set_include_path( APPLICATION_PATH "/library" . PATH_SEPARATOR . get_include_path() );
includePaths.library = APPLICATION_PATH "/library"
; Notez bien l'underscore final "_", il est ABSOLUMENT necessaire
autoloadernamespaces[] = "Model_"

2 - Dans le dossier “InvokableDataSet”, créer un fichier MyInvokable.php (remplacer MyInvokable par le nom souhaité) et un fichier MyInvokable.xml

3 - Dans MyInvokable.xml, décrire la structure xml du dataset ou du dataquery à partir duquel sera créé le dataset.

Exemple : Création d’un invokable Roles qui permet de filter les rôles selon les besoins Cet invokable dataset se base sur un dataquery.

<?xml version="1.0" encoding="UTF-8"?>
<!-- Fichier Roles.xml dans le dossier library/Model/InvokableDataSet de l'application -->
<dataquery id="xx" table_name="{pj}_pj_role" table_alias="r" mode="r">
    <column_simple field_name="id_role" table_name="r" />
    <column_simple field_name="name" table_name="r" />
    <column_simple field_name="label" table_name="r" />
</dataquery>

4 - Dans MyInvokable.php :

  • Créer une classe Model_InvokableDataSet_MyInvokable. Le nom de la classes doit être respecté : “Model_InvokableDataSet_” + “NomDuFichier”.
  • La faire hériter de la classe Core_Library_Model_InvokableDataSet. Il s’agit de la classe mère de tous les invokables datasets, elle propose plusieurs helpers.
  • Setter $this->_sXmlFilePath pour renseigner le chemin vers le fichier xml qui sera utilisé pour construire l’invokable (sauf si la classe hérite d’un invokable existant et que le xml correspondant convient déjà).
  • Créer une méthode transform() où sera placée la logique de traitement et de remplacement du dataset initial, ainsi que la gestion des paramètres. Attention : la classe Core_Library_Model_InvokableDataSet implémente plusieurs helpers mais aucune logique n’est pré-définie pour la méthode transform() -> même si le dataset décrit dans le xml ne doit subir aucune transformation, il faut au minimum retourner le dataset invoqué.

Exemple : suite -> Je spécifie le nom du fichier xml contenant la description du dataquery, j’implémente ma logique de filtrage et je retourne le dataset final.

// Fichier Roles.php dans le dossier library/Model/InvokableDataSet de l'application
// Exemple de surcharge de l'invokable dataset du noyau Roles pour permettre de filtrer les roles visibles

class Model_InvokableDataSet_Roles extends Core_Library_Model_InvokableDataSet {
    const EXCLUDE = 'exclude';

    public function __construct( array $aOptions = null ) {
        parent::__construct( $aOptions );
	// Le xml décrivant le dataquery sur lequel sera basé le dataset est dans le même dossier
        $this->_sXmlFilePath = str_replace('.php', '.xml', __FILE__);
    }

    /**
     * @return Core_Library_Resource_XML_DataSet
     */
    public function transform() {
        /** @var Core_Library_Resource_XML_DataQuery $oXMLDataQuery */
        $oXMLDataQuery = Core_Library_Resource_XML_DataQuery::CreateFromSimpleXMLObject( $this->GetProject(), $this->GetXmlDescription(), 'Core_Library_Resource_XML_DataQuery' );
        $oInvokedDataset = $this->GetInvokedDataset( $oXMLDataQuery );

        $aExclude = [];

        foreach ( $this->GetDatasetParams() as $sKey => $sValue ) {
            switch ( $sKey ) {
                case self::EXCLUDE:
                    $sValue = str_replace( ' ', '', $sValue );
                    $aExclude = explode( ',', $sValue );
                    break;
                default:
                    break;
            }
        }
        if ( !empty( $aExclude ) ) {
            $oRowData = $oInvokedDataset->GetRowData();

            // Check and save cursor position
            $iCursor = $oRowData->RecNo() > 0 ? $oRowData->RecNo() : '0';
            $iCursor = $iCursor < $oRowData->RecordCount() ? $iCursor : $oRowData->RecordCount() - 1;

            $oRowData->First();
            while ( !$oRowData->EoF() ) {
                if ( in_array( $oRowData->GetFieldValue( 'name' ), $aExclude ) ) {
                    $oRowData->RmRecord();
                    continue;
                }
                $oRowData->Next();
            }
            
            // Replace cursor to the same place
            $iCursor = $iCursor < $oRowData->RecordCount() ? $iCursor : $oRowData->RecordCount() - 1;
            $oRowData->MyGoto($iCursor);
        }

        return $oInvokedDataset;
    }
}

5 - L’invokable peut désormais être invoqué via son identifiant. Cet identifiant correspond à la dernière partie du nom de la classe qui le décrit.**

Exemple : suite -> j’appelle l’invokable “Roles” et j’exclus les rôle Adminustrator et Chef de Projet.

<dataset id="filtered_roles" invoke_from="Roles">
    <params>
        <value alias="exclude">admin, chef_projet</value>
    </params>
</dataset>	

Plusieurs invokable existent dans le noyau et peuvent servir d’exemple. Ils sont disponibles dans le dossier src > library > Model > InvokableDataSet du repo Voozanoo4.

A NOTER

La méthode \_executeInvokableDataset de la classe Core_Library_Model_Manager qui traite les datasets présentant l’attribut “invoke_from” fait appel aux méthodes transform() et checkformat() si elle trouve une classe correspondant à l’invokable. Ces méthodes doivent être présentes.

Par ailleurs, pour trouver l’invokable dataset appelé, elle regarde :

  1. Dans le code spécifique de l’application
  2. En base de données (source de donnée définie via epicraft)
  3. Dans le code source du noyau

Liens utiles vers le code source du noyau

Les invokable dataqueries

Utilisation

Tout comme pour les invokable datasets, les invokables dataqueries sont appelés au moyen de l’attribut invoke_from.

Le nom du dataquery invoqué correspond :

  • à son identifiant de ressource (Resource ID) s’il a été créé via epicraft. Cette information est disponible dans la liste des ressources du projet au sein de l’application - colonne Resource ID / Identifiant de la ressource - ou en base dans la colonne resource_id de la table {pj}_resource_data.
  • au nom du fichier xml où ce dataquery est décrit s’il a été créé dans le code source de l’application.

Il est possible de passer des balises et des attributs supplémentaires au dataquery appelé. Les balises seront rajoutées à la fin du dataquery invoqué. Les balises et attributs disponibles sont les mêmes que pour un dataquery.

Syntaxe générale :

<dataquery id="mon_dataquery" invoke_from="invokable_dataquery"/>

Exemple avec balises et attributs supplémentaires :

<!-- Filtre sur l'invokable liste_des_patients pour n'avoir que les patients de l'hôpital dont l'utilisateur est en train de consulter la fiche + pagination à 10 patients -->
<dataquery id="patients" invoke_from="liste_des_patients" begin="0" range="10">
    <condition sql="{id_hopital_patient}={id_hopital_courant}">
        <field field_name="id_hopital" alias="id_hopital_patient" table_name="alias_de_la_table_dans_l_invokable_dataquery"/>
        <variable alias="id_hopital_courant">
            <entry type="param" name="id_hopital" />
            <entry type="dataset" name="hopital" row="current" field="id_data" />
        </variable>
    </condition>
</dataquery>

Création

Il est possible de créer des invokables dataqueries spécifiques au niveau d’une application ou au niveau d’un projet.

Invokable dataquery spécifique à un projet -> Création via Epicraft

1 - Dans epicraft, créer une ressource de type xml au sein d’un projet, y décrire le dataquery souhaité. Par exemple :

<!--L'id sera remplacé par l'id du dataset qui invoquera ce dataset-->
<!-- Liste des patients -->
<?xml version="1.0" encoding="UTF-8"?>
<dataquery id="xxx" table_name="{pj}_patient_data" varset_name="patient" table_alias="p" mode="r" begin="0" range="10" >
    <column_simple table_name="p" field_name="id_data"/>
    <column sql="CONCAT({p.prenom}, ' ', {p.nom})" alias="prenom_nom" type="string">
        <field table_name="p" field_name="nom" alias="p.nom" mode="r"/>
        <field table_name="p" field_name="prenom" alias="p.prenom" mode="r"/>
    </column>
    <column_simple table_name="p" field_name="ddn"/>
    <column_simple table_name="p" field_name="id_hopital"/>
    <join detail_table="{pj}_hopital_data" detail_alias="h" detail_varset_name="hopital" sql="{p.id_hopital}={h.id_data}">
        <field table_name="h" field_name="id_data" alias="h.id_data"/>
        <field table_name="p" field_name="id_hopital" alias="p.id_hopital"/>
    </join>
    <column sql="{h.nom}" alias="nom_hopital" type="string">
        <field table_name="h" field_name="nom" alias="h.nom"/>
    </column>
</dataquery>

2 - Récupérer l’id de la ressource dans la barre d’adresse du navigateur : https://epicraft.voozanoo.net/client/#/edit-gui/dutvgjsoot1550590800637/bjsoefzdhm1565107626594.

Cette information est également récupérable après publication en base de données - colonne resource_id dans la table {pj}_resource_data - ou dans la liste des ressources du projet au sein de l’application - colonne Resource ID / Identifiant de la ressource.

3 - Utiliser cet identifiant pour invoquer le dataquery :

<dataquery id="liste_patients" invoke_from="bjsoefzdhm1565107626594" />

Invokable dataquery spécifique à une application -> Création dans le code source de l’application

Pour créer un invokabke dataquery au sein d’une application, il suffit de :

1 - Créer fichier xml décrivant le dataquery dans le code source de l’application, plus précisément, dans le dossier “library” > “Model” > “InvokableDataquery” (créer les dossiers s’ils n’existent pas).

2 - Si ce n’est pas déjà le cas, ajouter le namespace “Model_” dans l’application.ini :

; équivalent à un set_include_path( APPLICATION_PATH "/library" . PATH_SEPARATOR . get_include_path() );
includePaths.library = APPLICATION_PATH "/library"
; Notez bien l'underscore final "_", il est ABSOLUMENT necessaire
autoloadernamespaces[] = "Model_"

3 - Invoquer le dataquery créé via le nom de son fichier xml sans l’extension .xml : Le noyau gère la fusion des dataqueries inital et invoqué et la transformation en dataset.

Liens utiles vers le code source du noyau