version 11.2 (Modifiée)
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.
Activer et créer un trigger
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.
1. Activer un Trigger
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 :
Sur sauvegarde nouvel enregistrement
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 AJOUTER ENREGISTREMENT ou commande SQL INSERT).
Vous créez puis sauvegardez un enregistrement avec CREER ENREGISTREMENT et STOCKER ENREGISTREMENT. Notez que le trigger est appelé au moment où vous exécutez STOCKER ENREGISTREMENT, 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 TABLEAU VERS SELECTION, STOCKER SUR LIEN, etc.).
Vous utilisez un plug-in 4D qui appelle les commandes CREER ENREGISTREMENT et STOCKER ENREGISTREMENT.
Sur sauvegarde enregistrement
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 MODIFIER ENREGISTREMENT ou commande SQL UPDATE)
Vous sauvegardez un enregistrement existant avec STOCKER ENREGISTREMENT.
Vous appelez une commande qui provoque la sauvegarde d'un enregistrement existant (par exemple, TABLEAU VERS SELECTION, APPLIQUER A SELECTION, etc.)
Vous utilisez un plug-in 4D qui appelle la commande STOCKER ENREGISTREMENT.
Sur suppression enregistrement
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 SUPPRIMER ENREGISTREMENT, SUPPRIMER 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 SUPPRIMER ENREGISTREMENT.
Note : La commande VIDER TABLE ne provoque PAS l'appel du trigger.
2. Créer un trigger
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.
Evénements moteur
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 Evenement moteur. 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 Evenement moteur :
` Trigger pour [UneTable] C_ENTIER LONG($0) $0:=0 ` On suppose que la requête est acceptée Au cas ou : (Evenement moteur=Sur sauvegarde nouvel enreg) ` Effectuer les actions appropriées pour sauvegarder l'enregistrement nouvellement créé. : (Evenement moteur=Sur sauvegarde enregistrement) ` Effectuer les actions appropriées pour sauvegarder l'enregistrement déjà existant. : (Evenement moteur=Sur suppression enregistrement) ` Effectuer les actions appropriées pour détruire l'enregistrement. Fin de cas
Les triggers sont des fonctions
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.
1. Effectuer des actions
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] Au cas ou : (Evenement moteur=Sur sauvegarde nouvel enreg) [Documents]Creation Stamp:=Time stamp [Documents]Modification Stamp:=Time stamp : (Evenement moteur=Sur sauvegarde enregistrement) [Documents]Modification Stamp:=Time stamp Fin de cas
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 PROPRIETES DOCUMENT pour une analyse complète de cet exemple.
2. Accepter ou rejeter l'opération de la base
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 Si (Bon No Séc.Soc ([Employés]No Séc.Soc)) VALIDER Sinon BEEP ALERTE ("Saisissez un numéro de sécurité sociale et cliquez de nouveau sur OK.") Fin de si
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 ` ... CREER ENREGISTREMENT ([Employés]) [Employés]Nom :="DOE" STOCKER ENREGISTREMENT ([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:=Evenement moteur Au cas ou : (($dbEvent=Sur sauvegarde nouvel enreg) | ($dbEvent=Sur sauvegarde enregistrement)) Si (Non(Bon No Séc.Soc ([Employés]No Séc.Soc))) $0:=-15050 Sinon ` ... Fin de si ` ... Fin de cas
Une fois que ce trigger est écrit et activé, la ligne STOCKER ENREGISTREMENT([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, 4D Open client avec 4D Server) 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 APPELER SUR ERREUR 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 SUPPRIMER 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 et l'architecture 4D
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, statut lecture/écriture des tables...). 4D Server garantit également 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.
Triggers et transactions
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 Transaction en cours. 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 VALIDER TRANSACTION. Cela évite que les triggers soient exécutés deux fois.
Triggers en cascade
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 à SUPPRIMER 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 Niveau du trigger. De plus, vous pouvez aussi obtenir des informations sur les autres niveaux en utilisant la commande PROPRIETES DU TRIGGER.
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 Niveau du trigger et PROPRIETES DU TRIGGER, 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.
Utilisation de la numérotation automatique dans un trigger
Quand vous manipulez l'événement moteur Sur sauvegarde nouvel enreg, vous pouvez appeler la commande Numerotation automatique pour maintenir un numéro d'identification unique pour chaque enregistrement d'une table. Par exemple :
` Trigger pour la table [Factures] Au cas ou : (Evenement moteur=Sur sauvegarde nouvel enreg) ` ... [Factures]Facture Identification:=Numerotation automatique ([Factures]) ` ... Fin de cas
Référence
Evenement moteur, Méthodes, Niveau du trigger, Numero enregistrement, PROPRIETES DU TRIGGER.