4D v16.3

Présentation des triggers

Accueil

 
4D v16.3
Présentation des triggers

Présentation des triggers  


 

 

Un trigger est une méthode associée à une table. C'est une propriété d'une table. Vous n'appelez pas un trigger, les triggers sont appelés automatiquement par le moteur de 4D à chaque fois qu'un enregistrement de la table est manipulé (ajout, suppression et modification). Les triggers sont des méthodes qui peuvent éviter des opérations “illégales” dans votre base. Par exemple, dans une facturation, vous pouvez empêcher qu'un utilisateur crée une facture sans spécifier à qui elle doit être adressée. Les triggers sont un outil puissant permettant de contrôler les opérations sur les tables, et d'éviter des pertes de données accidentelles. Vous pouvez créer des triggers très simples et les rendre de plus en plus sophistiqués.

Note : Consultez le document 4D Security guide pour une vue d'ensemble des fonctions de sécurité de 4D.

Par défaut, lorsque vous créez une table en mode Développement, la table n'a pas de trigger.

Pour utiliser un trigger pour une table, vous devez :

  • activer le trigger et indiquer à 4D quand l'appeler.
  • créer et écrire le code pour le trigger.

Activer un trigger qui n'est pas encore écrit ou écrire un trigger sans l'activer n'affecte pas les opérations effectuées sur une table.

Pour activer le trigger, sélectionnez les options Triggers pour la table dans la fenêtre de l'Inspecteur de structure :

Voici la description de ces options :

Si cette option est sélectionnée, le trigger sera appelé à chaque fois qu'un enregistrement de la table est modifié, c'est-à-dire dans les circonstances suivantes :

  • Vous modifiez un enregistrement en saisie de données (mode Développement, commande MODIFY RECORD ou commande SQL UPDATE)
  • Vous sauvegardez un enregistrement existant avec SAVE RECORD.
  • Vous appelez une commande qui provoque la sauvegarde d'un enregistrement existant (par exemple, ARRAY TO SELECTION, APPLY TO SELECTION, etc.)
  • Vous utilisez un plug-in 4D qui appelle la commande SAVE RECORD.
Note : Pour des raisons d'optimisation, le trigger n'est pas appelé lors de la sauvegarde de l'enregistrement par l'utilisateur ou via la commande SAVE RECORD si aucun champ de la table n'a été modifié dans l'enregistrement. Si vous souhaitez "forcer" l'appel du trigger dans ce cas, il suffit d'auto-affecter un champ :
 [latable]lechamp:=[latable]lechamp

Si cette option est sélectionnée, le trigger sera appelé à chaque fois qu'un enregistrement de la table est supprimé, c'est-à-dire dans les circonstances suivantes :

  • Vous supprimez un enregistrement en mode Développement ou en appelant la commande DELETE RECORD, DELETE SELECTION ou la commande SQL DELETE.
  • Vous effectuez des opérations qui provoquent la suppression d'un enregistrement lié par l'intermédiaire des options de contrôle de suppression d'un lien.
  • Vous utilisez un plug-in 4D qui appelle la commande DELETE RECORD.

Note : La commande TRUNCATE TABLE ne provoque PAS l'appel du trigger.

Si cette option est sélectionnée, le trigger sera appelé à chaque fois qu'un enregistrement est créé dans la table, c'est-à-dire dans les circonstances suivantes :

  • Vous ajoutez un enregistrement lors de la saisie de données (mode Développement, commande ADD RECORD ou commande SQL INSERT).
  • Vous créez puis sauvegardez un enregistrement avec CREATE RECORD et SAVE RECORD. Notez que le trigger est appelé au moment où vous exécutez SAVE RECORD, et non quand il est réellement créé.
  • Vous importez des enregistrements (mode Développement ou commandes du langage).
  • Vous appelez d'autres commandes qui créent et/ou sauvegardent de nouveaux enregistrements (par exemple ARRAY TO SELECTION, SAVE RELATED ONE, etc.).
  • Vous utilisez un plug-in 4D qui appelle les commandes CREATE RECORD et SAVE RECORD.

Pour créer un trigger pour une table, utilisez la fenêtre de l'Explorateur, cliquez sur le bouton Editer dans la palette de l'Inspecteur de structure ou double-cliquez sur le titre de la table dans la fenêtre de Structure en appuyant sur la touche Alt (sous Windows) ou Option (sous Mac OS). Pour plus d'informations, reportez-vous au manuel Mode Développement.

Un trigger peut être invoqué pour l'un des trois événements moteur décrits ci-dessus. Dans le trigger, vous détectez quel événement a lieu en appelant la fonction Trigger event. Cette fonction retourne une valeur numérique qui indique l'événement moteur.

Typiquement, vous écrivez un trigger avec une structure du type "Au cas ou" sur le résultat retourné par Trigger event :

  // Trigger pour [UneTable]
 C_LONGINT($0)
 $0:=0 // On suppose que la requête est acceptée
 Case of
    :(Trigger event=On Saving New Record Event)
  // Effectuer les actions appropriées pour sauvegarder l'enregistrement nouvellement créé.
    :(Trigger event=On Saving Existing Record Event)
  // Effectuer les actions appropriées pour sauvegarder l'enregistrement déjà existant.
    :(Trigger event=On Deleting Record Event)
  // Effectuer les actions appropriées pour détruire l'enregistrement.
 End case

Un trigger a deux finalités :

  • Effectuer des actions sur l'enregistrement juste avant qu'il soit sauvegardé ou supprimé.
  • Accepter ou rejeter une opération de base de données.

A chaque fois qu'un enregistrement est sauvegardé (ajouté ou modifié) dans une table [Documents], vous souhaitez “estampiller ” l'enregistrement avec des marqueurs de création et de modification. Vous pouvez écrire le trigger suivant :

  // Trigger pour table [Documents]
 Case of
    :(Trigger event=On Saving New Record Event)
       [Documents]Creation Stamp:=Time stamp
       [Documents]Modification Stamp:=Time stamp
    :(Trigger event=On Saving Existing Record Event)
       [Documents]Modification Stamp:=Time stamp
 End case

Note : La fonction Time stamp utilisée dans cet exemple est une petite méthode projet retournant le nombre de secondes écoulées depuis une date choisie arbitrairement.

Une fois que ce trigger a été écrit et activé, peu importe la façon dont vous ajoutez ou modifiez un enregistrement dans la table [Documents] (saisie de données, import, méthode projet, plug-in 4D), la valeur des deux champs [Documents]Creation Stamp et [Documents]Modification Stamp sera automatiquement affectée par le trigger avant que l'enregistrement ne soit écrit sur disque.

Note : Voir l'exemple de la commande GET DOCUMENT PROPERTIES pour une analyse complète de cet exemple.

Pour accepter ou rejeter une opération de la base, le trigger doit retourner un code d'erreur de trigger dans le résultat de la fonction $0.

Exemple  

Prenons le cas d'une table [Employés]. Pendant la saisie de données, vous contrôlez le champ [Employés]No Séc.Soc. Par exemple, lorsque l'utilisateur clique sur le bouton de validation, vous vérifiez le champ utilisant la méthode objet du bouton : 

  // Méthode objet bouton bAccept
 If(Bon No Séc.Soc([Employés]No Séc.Soc))
    ACCEPT
 Else
    BEEP
    ALERT("Saisissez un numéro de sécurité sociale et cliquez de nouveau sur OK.")
 End if

Si la valeur du champ est correcte, vous acceptez la saisie de données, sinon vous affichez une alerte et restez en saisie de données.

Si vous créez aussi des enregistrements pour la table [Employés] par programmation, le code ci-dessous serait valide MAIS violerait la règle imposée dans la méthode objet créée plus haut :

  ` Extrait d'une méthode projet
  ` ...
 CREATE RECORD([Employés])
 [Employés]Nom:="DOE"
 SAVE RECORD([Employés]` <- violation de la règle ! Il n'y a pas de numéro de sécurité sociale !

En utilisant un trigger pour la table [Employés], vous pouvez appliquer la contrainte sur [Employés]No Séc.Soc à tous les niveaux de la base. Le trigger serait du type :

  // Trigger pour [Employés]
 $0:=0
 $dbEvent:=Trigger event
 Case of
    :(($dbEvent=On Saving New Record Event)|($dbEvent=On Saving Existing Record Event))
       If(Not(Bon No Séc.Soc([Employés]No Séc.Soc)))
          $0:=-15050
       Else
  // ...
       End if
  // ...
 End case

Une fois que ce trigger est écrit et activé, la ligne SAVE RECORD([Employés]) de la méthode projet ci-dessus génèrera une erreur moteur -15050 et l'enregistrement ne sera PAS sauvegardé.

De la même façon, si un plug-in 4D essayait de sauvegarder un enregistrement dans [Employés] avec un numéro de sécurité sociale incorrect, le trigger génèrerait la même erreur et l'enregistrement ne serait pas sauvegardé non plus.

Le trigger garantit que personne (utilisateur, développeur, plug-in...) ne peut violer la règle sur le numéro de sécurité sociale (à dessein ou par erreur).

Notez que même si vous n'avez pas créé de trigger pour une table, la base peut retourner des erreurs moteur lorsque vous essayez de sauvegarder ou de détruire un enregistrement. Vous pouvez, par exemple, recevoir l'erreur -9998, si vous essayez de sauvegarder un enregistrement.

Les triggers retournent de nouveaux types d'erreurs dans 4D :

  • 4D gère les erreurs “normales” : index unique, contrôles relationnels, etc.
  • En utilisant les triggers, vous pouvez créer des codes d'erreurs propres au contenu de votre application.

Important : Vous pouvez retourner le code d'erreur de votre choix. Cependant, n'utilisez pas des codes d'erreurs déjà utilisés par le moteur de 4D. Nous vous recommandons fortement d'utiliser des codes compris entre -32000 et -15000. Nous réservons les erreurs supérieures à -15000 au moteur de 4D.

Au niveau du process, vous gérez les erreurs trigger de la même façon que les erreurs du moteur de base de données :

  • vous pouvez laisser 4D afficher la boîte de dialogue standard d'erreur, la méthode est alors interrompue.
  • vous pouvez utiliser une méthode de gestion d'erreur installée par ON ERR CALL et traiter l'erreur de façon appropriée.

Notes :

  • Pendant la saisie, si une erreur trigger est retournée au moment où vous essayez de valider ou de supprimer un enregistrement, l'erreur est gérée comme une erreur sur un index unique. La boîte de dialogue d'erreur est affichée et vous restez en saisie de données. Même si vous n'utilisez une base qu'en mode Développement (et non en Application), vous bénéficiez des triggers.
  • Lorsqu'une erreur est générée par un trigger dans le cadre d'une commande agissant sur une sélection d'enregistrements (telle que DELETE SELECTION), l'exécution de la commande est immédiatement stoppée, sans que la sélection ait été nécessairement traitée en totalité. Ce cas requiert de la part du développeur une gestion appropriée, basée par exemple sur la conservation temporaire de la sélection, le traitement et l'élimination de l'erreur avant l'exécution du trigger, etc.

Même si un trigger ne retourne pas d'erreur ($0:=0), cela ne signifie pas qu'une opération de la base s'effectuera correctement. Il peut y avoir eu un doublon sur l'index unique. Si l'opération est la mise à jour d'un enregistrement, ce dernier peut être verrouillé, une erreur d'entrée/sortie peut se produire, bien d'autres choses encore peuvent arriver. Ces vérifications sont effectuées après l'exécution du trigger. Cependant, du point de vue du plus haut niveau du process en exécution, les erreurs retournées par le moteur de la base de données ou celle d'un trigger sont de même nature : une erreur trigger est une erreur du moteur de la base de données.

Les triggers sont exécutés au niveau du moteur de la base de données. Ce point est illustré dans le schéma suivant :

Les triggers sont exécutés sur la machine où est situé le moteur de la base de données. Si ce point est une évidence dans le cas de 4D en local, il convient de rappeler que pour 4D Server, les triggers sont exécutés sur la machine serveur (dans le process "jumeau" du process ayant déclenché le trigger) et non sur la machine cliente.

Quand un trigger est appelé, il s'exécute dans le contexte du process qui tente l'opération. Ci-dessous, ce process est appelé process appelant l'exécution du trigger.
Les éléments inclus dans ce contexte diffèrent suivant que la base est exécutée avec 4D en mode local ou avec 4D Server :

  • avec 4D en mode local, le trigger fonctionne avec les sélections courantes, les enregistrements courants, les statuts lecture/écriture des tables, les verrouillages d'enregistrements, etc., du process appelant.
  • avec 4D Server, seul le contexte de base de données du process client appelant est préservé (verrouillages d'enregistrements et statut transactionnel). 4D Server garantit également (et uniquement) que l'enregistrement courant de la table du trigger est correctement positionné. Les autres éléments contextuels (sélections courantes par exemple) sont ceux du process du trigger.

Soyez prudent lorsque vous utilisez les autres objets de la base et du langage, car un trigger peut s'exécuter sur une machine différente de celle du process appelant : c'est le cas avec 4D Server !

  • Variables interprocess : Un trigger a accès aux variables interprocess de la machine où il est exécuté. Avec 4D Server, ce peut être une machine différente de celle du process appelant.
  • Variables process : Chaque trigger possède sa propre table de variables process. Un trigger n'a pas accès aux variables process du process appelant.
  • Variables locales : Vous pouvez utiliser des variables locales dans un trigger. Leur aire d'action est l'exécution du trigger (elles sont créées/détruites au cours de cette exécution).
  • Sémaphores : Un trigger peut tester ou placer des sémaphores globaux et locaux (sur la machine où il s'exécute dans ce dernier cas). Cependant, un trigger doit s'exécuter rapidement. En conséquence, utilisez plutôt des sémaphores locaux dans un trigger, sauf si vous avez une idée précise en tête.
  • Ensembles et sélections temporaires : Si vous utilisez un ensemble ou une sélection temporaire dans un trigger, vous travaillez alors avec ceux de la machine où les triggers s'exécutent. En client/serveur, les ensembles et sélections temporaires "process" (dont le nom ne débute ni par $ ni par <>) créés sur le client sont visibles dans un trigger.
  • Interface utilisateur : N'utilisez PAS d'éléments d'interface utilisateur dans un trigger (alerte, message ou dialogue). Cela signifie également que tracer le trigger dans la fenêtre du Débogueur doit être limité. Souvenez-vous que les triggers en client/serveur s'exécutent sur la machine 4D Server. Un message d'alerte affiché sur le poste serveur ne dit pas grand chose à l'utilisateur qui, lui, travaille sur sa machine cliente. Laissez le process appelant gérer l'interface utilisateur.

A noter que si vous utilisez le système de mots de passe de 4D, vous pouvez exécuter la commande Current user dans le trigger afin, par exemple, de stocker dans une table journal le nom de l'utilisateur à l'origine de l'appel du trigger, y compris en mode client-serveur.

Les transactions doivent être gérées au niveau du process appelant. Il est fortement déconseillé de gérer des transactions au niveau du trigger. Si, pendant l'exécution d'un trigger, vous devez ajouter, modifier ou détruire plusieurs enregistrements et souhaitez garantir l'intégrité de vos données à l'aide d'une transaction, vous devez d'abord tester (à partir du trigger) si le process appelant est en cours de transaction avec la commande In transaction. En effet, si ce n'est pas le cas et si le trigger rencontre un enregistrement verrouillé, le process appelant n'aura aucun moyen d'annuler a posteriori les actions déjà effectuées par le trigger. Par conséquent, si vous n'êtes pas en transaction, ne commencez pas les opérations à exécuter, et retournez simplement une erreur dans $0 afin de signaler au process appelant que l'opération de base de données doit être exécutée dans une transaction.

Note : Afin d'optimiser le fonctionnement combiné des triggers et des transactions, 4D n'appelle PAS les triggers lors d'un VALIDATE TRANSACTION. Cela évite que les triggers soient exécutés deux fois.

Prenons l'exemple de la structure suivante :

Note : Les tables ont été contractées (il y a davantage de champs).

Pour les nécessités de cette documentation, nous admettrons que la base “autorise” la suppression d'une facture. Voyons aussi comment une telle opération serait gérée au niveau du trigger (puisque vous pourriez aussi décider d'effectuer l'opération au niveau du process).

Afin que soit maintenue l'intégrité relationnelle des données, la suppression d'une facture requiert les actions suivantes de la part du trigger de [Factures] :

  • Décrémenter le champ Ventes de la table [Clients] du montant de la facture.
  • Supprimer tous les enregistrements de [Lignes facture] liés à la facture.
  • Ceci implique aussi que le trigger de [Lignes facture] décrémente le champ Quantité vendue des enregistrements [Produits] liés à la ligne de facture que l'on s'apprête à supprimer.
  • Supprimer tous les enregistrements de [Paiements] liés à la facture.

Tout d'abord, le trigger de [Factures] ne doit effectuer ces actions que si le process appelant est en transaction, afin qu'une annulation rétroactive soit possible en cas de rencontre d'un enregistrement verrouillé.

Deuxièmement, le trigger de [Lignes facture] est en cascade avec le trigger de [Factures]. Le premier s'exécute “à l'intérieur” du second parce que la destruction des éléments de la liste est consécutive à un appel à DELETE SELECTION dans le trigger de [Factures].

Ajoutons que toutes les tables dans cet exemple ont des triggers activés pour tous les événements de la base de données. La cascade des triggers sera :

  • Le trigger de Factures est appelé car le process appelant supprime une facture
    • Le trigger de Clients est appelé car le trigger Factures met à jour le champ Ventes
    • Le trigger de Lignes facture est appelé car le trigger Factures supprime une ligne (ce qui est répété)
      • Le trigger de Produits est appelé car le trigger Lignes facture met à jour le champ Quantité vendue
    • Le trigger de Paiements est appelé car le trigger Factures supprime un paiement (ce qui est répété)
Dans cette cascade, le trigger de [Factures] s'exécute au niveau 1, les triggers [Clients], [Lignes facture] et [Paiements] au niveau 2 et le trigger [Produits] au niveau 3.

Dans les triggers, vous pouvez détecter à quel niveau un trigger est exécuté grâce à la commande Trigger level. De plus, vous pouvez aussi obtenir des informations sur les autres niveaux en utilisant la commande TRIGGER PROPERTIES.

Si, par exemple, vous détruisiez un enregistrement [Produits] à un niveau process, le trigger de [Produits] s'exécuterait au niveau 1, non au niveau 3, comme plus haut.

Avec Trigger level et TRIGGER PROPERTIES, vous pouvez identifier la raison d'une action. Dans l'exemple ci-dessus, une facture est supprimée au niveau process. Prenons pour hypothèse que nous voulons détruire un enregistrement [Clients] au niveau process. Le trigger de [Clients] devrait alors être conçu pour détruire toutes les factures liées à ce client. Cela signifie que le trigger [Factures] devrait être invoqué comme plus haut, mais pour une autre raison. Du trigger [Factures], vous pouvez détecter si le niveau est 1 ou 2. S'il est 2, vous pouvez vérifier si oui ou non c'est à cause de la suppression de l'enregistrement Client lui-même. Si tel est le cas, vous n'avez même plus besoin de vous préoccuper de la mise à jour du champ Ventes.



Voir aussi  

Méthodes
Record number
Trigger event
Trigger level
TRIGGER PROPERTIES

 
PROPRIÉTÉS 

Produit : 4D
Thème : Triggers

 
HISTORIQUE 

 
UTILISATION DE L'ARTICLE

4D - Langage ( 4D v16)
4D - Langage ( 4D v16.1)
4D - Langage ( 4D v16.2)
4D - Langage ( 4D v16.3)