HOWTO développement Sashipa

 Gérer deux niveaux de clefs-étrangères.

Dans le même écran que votre cardForm, vous souhaitez afficher une listForm qui contient les données qui référencent les données d'une autre table, qui référence la table de votre cardForm.

Préparation

Voici tout d'abord le script SQL de la base sur laquelle on va travailler (ici avec la syntaxe HSQL) :

  CREATE TABLE Organisme (
    Id_Organisme integer not null primary key,
    Nom varchar(100) not null
  );
  CREATE TABLE Section (
    Id_Section integer not null primary key,
    Ref_Organisme integer not null,
    Nom varchar(100) not null,
    FOREIGN KEY (Ref_Organisme) REFERENCES Organisme (Id_Organisme)
  );
  CREATE TABLE Personne (
    Id_Personne integer not null primary key,
    Ref_Section integer not null,
    Nom varchar(100) not null,
    FOREIGN KEY (Ref_Section) REFERENCES Section (Id_Section)
  );

Nous avons des Organismes qui contiennent plusieurs Sections, qui contiennent elles-même plusieurs Personnes. Nous souhaitons afficher la liste des Personnes dans la fiche Organisme.

Problématique :

L'affichage simple des personnes dans une listForm est relativement simple en soit (il suffit d'une jointure), l'affaire se complique si l'on souhaite permettre l'ajout d'une personne depuis le bouton ajouter de la listForm. En effet, dans ce cas il est préférable que le choix des sections dans la fiche Personne, se limite aux sections de l'organisme.

La fiche Organisme

Commençons par le code de la fiche Organisme :

  <screen name='SCtblOrganisme'>
    <title>Fiche d'un organisme</title>
    <formSet>

      <cardForm db='dbDemo'>
        <title>Fiche d'un organisme</title>
        <location x='10' y='10' />
        <cardSchemaTableRef schemaTable='tblOrganisme' queries='sudi'
                            updateAfterInsert='yes' multipleInsert='no' />
        <fieldContainer>
          <textField>
            <schemaColumnRef schemaColumn='tblOrganisme_Nom' />
          </textField>
        </fieldContainer>
      </cardForm>
      
      <!-- ... listForm des sections ... -->
  
      <listForm db='dbDemo' doubleClicScreen='SCtblPersonne' 
                delete='yes'>
        <title>Personnes (Sections)</title>
        <bounds x='360' y='10' w='300' h='400' />
        <selectQueryBuilder type='list'>
          <castFilterSet autoCastFilter='no'>
            <fkCastFilter>
              <instanceFk schemaFk='fk_tblSection_tblOrganisme' />
            </fkCastFilter>
          </castFilterSet>
          <fromStatementBuilder>
            <mainInstanceTable schemaTable='tblPersonne' />
            <fkJoin join='inner'>
              <instanceFk schemaFk='fk_tblPersonne_tblSection' />
            </fkJoin>
          </fromStatementBuilder>
        </selectQueryBuilder>
        <instanceColumnList>
          <instanceColumn schemaColumn='tblSection_Nom' >
            <title>Direction</title>
          </instanceColumn>
          <instanceColumn schemaColumn='tblPersonne_Nom' />
        </instanceColumnList>
        <insertScreen screen='SCtblPersonne' 
                      schemaFk='fk_tblPersonne_tblSection' />
      </listForm>

    </formSet>
  </screen>

Explications :

La fiche Section n'a rien de particulier, passons directement à la fiche Personne.

La fiche Personne

Dans cette fiche, la particularité est la requête de la combo-box qui propose les Sections. Focalisons-nous sur cette requête :

  <screen name='SCtblPersonne'>
    <title>Fiche d'une personne</title>
    <formSet>
      <cardForm db='dbDemo'>
        <title>Fiche d'une personne</title>
        <location x='10' y='10' />
        <cardSchemaTableRef schemaTable='tblPersonne' queries='sudi'
                            updateAfterInsert='no' multipleInsert='yes' />
        <fieldContainer>

          <comboBoxFkField>
            <schemaFkRef schemaFk='fk_tblPersonne_tblSection' />
            <go screen='SCtblSection' />
            <choose screen='SCtblSection' />

            <selectQueryBuilder distinctRequired='yes'>
              <castFilterSet autoCastFilter='no'>
                <pkCastFilter>
                  <instanceTable schemaTable='tblOrganisme' />
                </pkCastFilter>
                <pkCastFilter>
                  <instanceTable schemaTable='tblSection' 
                                 nickName='sec2' />
                </pkCastFilter>
                <pkCastFilter>
                  <instanceTable schemaTable='tblPersonne' 
                                 nickName='pers2' />
                </pkCastFilter>
              </castFilterSet>
              
              <fromStatementBuilder>
                <mainInstanceTable schemaTable='tblSection' />
                <fkJoin join='inner'>
                  <instanceFk schemaFk='fk_tblSection_tblOrganisme' />
                </fkJoin>
                <fkJoin join='inner'>
                  <instanceFk schemaFk='fk_tblSection_tblOrganisme'>
                    <startInstanceTable schemaTable='tblSection' 
                                        nickName='sec2' />
                  </instanceFk>
                </fkJoin>
                <fkJoin join='left'>
                  <instanceFk schemaFk='fk_tblPersonne_tblSection'>
                    <startInstanceTable schemaTable='tblPersonne' 
                                        nickName='pers2' />
                  </instanceFk>
                  <referencedInstanceTable schemaTable='tblSection' 
                                           nickName='sec2' />
                </fkJoin>
              </fromStatementBuilder>
            </selectQueryBuilder>

          </comboBoxFkField>

          <textField>
            <schemaColumnRef schemaColumn='tblPersonne_Nom' />
          </textField>
        </fieldContainer>
      </cardForm>
    </formSet>
  </screen>

Les castFilters...

Lors de l'ouverture d'une fiche en mode Ajout, la fiche n'est pas filtrée. Le filtre de l'écran appelant - s'il existe - circule tout de même mais les fkFields l'ignorent, à une exception près. Il est en effet possible de spécifier une clef étrangère lors de la déclaration de l'élément insertScreen de la fiche appelante. Si elle existe, alors le fkField qui travaille sur cette clef étrangère aura accès au filtre de l'appelant.

C'est par ce mécanisme que la combo-box de notre écran ici, pourra être filtrée en mode ajout avec le filtre de la fiche Organisme.

Voyons le rôle des différents castFilters :

La clause From...

Nous opérons une série de jointures. Nous partons de la table Section, nous faisons une jointure sur l'organisme référencé, puis une jointure sur l'ensemble des sections de cette organisme, puis une jointure gauche ('left') sur les personnes de ces sections.

En bref :

Section (défaut) - Organisme (défaut) - Section (sec2) - Personne (per2)

Les colonnes affichées (clause Select) seront celles de l'instance par défaut de la table Section. Les castFilters portent sur les trois autres instances de tables, dans le but d'afficher les sections de l'Organisme courant.

Un dernier mot à propos de la jointure de la table Personne :