Aller au contenu principal
Version : 21 BETA

Évènements d'entité

Historique
ReleaseModifications
21Événements ajoutés : validateSave / saving / afterSave / validateDrop / dropping / afterDrop
20 R10ajout événement touched

Les événements d'entité sont des fonctions qui sont automatiquement invoquées par ORDA chaque fois que des entités et des attributs d'entité sont touchés (ajoutés, supprimés ou modifiés). Vous pouvez écrire des événements très simples, puis les rendre plus sophistiqués.

Vous ne pouvez pas déclencher directement l'exécution d'une fonction d'événement. Les événements sont appelés automatiquement par ORDA en fonction des actions de l'utilisateur ou des opérations effectuées par le code sur les entités et leurs attributs.

Note de compatibilité

Les événements d'entité ORDA dans le magasin de données sont équivalents aux triggers dans la base de données 4D. Cependant, les actions déclenchées au niveau de la base de données 4D à l'aide des commandes du langage classique 4D ou des actions standard ne déclenchent pas les événements ORDA.

Vue d’ensemble

Niveau de l'événement

Une fonction d'événement d'entité est toujours définie dans la classe Entity.

Un événement peut être défini au niveau de l'entité et/ou de l'attribut (y compris les attributs calculés). Dans le premier cas, il sera déclenché pour tous les attributs de l'entité ; dans l'autre cas, il ne sera déclenché que pour l'attribut ciblé.

Pour un même événement, vous pouvez définir différentes fonctions pour différents attributs.

Vous pouvez également définir le même événement au niveau de l'attribut et de l'entité. L'événement attribut est appelé en premier, puis l'événement entité.

Exécution en configuration distante

En général, les événements ORDA sont exécutés sur le serveur.

Cependant, dans une configuration client/serveur, la fonction d'événement touched() peut être exécutée sur le serveur ou le client, en fonction de l'utilisation du mot-clé local. Une implémentation spécifique côté client permet de déclencher l'événement sur le client.

note

Les fonctions ORDA constructor() sont toujours exécutées sur le client.

With other remote configurations (i.e. Qodly applications, REST API requests, or requests through Open datastore), the touched() event function is always executed server-side. Cela signifie que vous devez vous assurer que le serveur peut "voir" qu'un attribut a été touché pour déclencher l'événement (voir ci-dessous).

Tableau de synthèse

Le tableau suivant liste les événements d'entité ORDA ainsi que leurs règles.

EvénementNiveauNom de la fonction(C/S) Exécuté surPeut arrêter l'action en renvoyant une erreur
Instanciation d'entitéEntityconstructor()clientnon
Attribut touchedAttributevent touched <attrName>()Dépend du mot-clé localnon
Entityevent touched()Dépend du mot-clé localnon
Avant l'enregistrement d'une entitéAttributvalidateSave <attrName>()serveroui
EntityvalidateSave()serveroui
Pendant l'enregistrement d'une entitéAttributsaving <attrName>()serveroui
Entitysaving()serveroui
Après l'enregistrement d'une entitéEntityafterSave()servernon
Avant la suppression d'une entitéAttributvalidateDrop <attrName>()serveroui
EntityvalidateDrop()serveroui
Pendant la suppression d'une entitéAttributdropping <attrName>()serveroui
Entitydropping()serveroui
Après la suppression d'une entitéEntityafterDrop()servernon
note

La fonction constructor() n'est pas en soi une fonction d'événement, mais elle est toujours appelée lorsqu'une nouvelle entité est instanciée.

Paramètre event

Les fonctions d'événement acceptent un seul objet event comme paramètre. Lorsque la fonction est appelée, le paramètre est rempli avec diverses propriétés :

Nom de propriétéDisponibilitéTypeDescription
"kind"ToujoursStringNom de l'événement : "touched", "validateSave", "saving", "afterSave", "validateDrop", "dropping", "afterDrop"
attributeNameUniquement pour les événements définis au niveau des attributs ("validateSave", "saving", "validateDrop", "dropping")StringNom de l'attribut (ex. "firstname")
dataClassNameToujoursStringNom du verre de données (ex. "Company")
"savedAttributes"Uniquement dans afterSave()Collection de chaînesNoms des attributs correctement enregistrés
"droppedAttributes"Uniquement dans afterDrop()Collection de chaînesNoms des attributs correctement supprimés
"saveStatus"Uniquement dans afterSave()String"success" si l'enregistrement a réussi, "failed" sinon
"dropStatus"Uniquement dans afterDrop()String"success" si la suppression a réussi, "failed" sinon

Objet error

Certaines fonctions d'événement peuvent renvoyer un objet error pour déclencher une erreur et arrêter l'action en cours.

Lorsqu'une erreur survient dans un événement, les autres événements sont stoppés à la première erreur signalée et l'action (enregistrement pu suppression) est également arrêtée. Cette erreur est envoyée avant d'autres erreurs potentielles telles que stamp has changed, entity locked, etc.

Propriétés de l'objet error

PropriétéTypeDescriptionFixé par le développeur
errCodeIntegerIdentique à la commande Last errorsOui
messageTextIdentique à la commande Last errorsOui
extraDescriptionObjectInformations libres à définirOui
seriousErrorBooleanUtilisé uniquement avec les événements de validation (voir ci-dessous).
  • True : crée une erreur critique (imprévisible) et déclenche une exception. Ajoute le statut dk status serious validation error
  • False : crée seulement une erreur silencieuse (prévisible). Ajoute le statut dk status validation failed.
  • Oui (par défaut : False)
    componentSignatureTextToujours "DBEV"Non
    • Les erreurs critiques sont empilées dans la collection de la propriété errors de l'objet Result renvoyé par les fonctions save() ou drop().
    • Dans le cas d'une erreur déclenchée par un événement validate, la propriété seriousError permet de choisir le niveau d'erreur à générer :
      • Si true : une erreur critique est déclenchée et doit être traitée par le code de traitement des erreurs, tel qu'un "try catch". Dans l'objet résultat de la fonction appelante, status vaut dk status serious validation error et statusText vaut "Serious Validation Error". L'erreur est levée à la fin de l'événement et parvient au client qui demande l'action d'enregistrement/suppression (client REST par exemple).
      • Si false (défaut) : une erreur silencieuse (prévisible) est générée. Elle ne déclenche aucune exception et n'est pas empilée dans les erreurs retournées par la commande Last errors. Dans l'objet résultat de la fonction appelante, status vaut dk status validation failed et statusText vaut "Mild Validation Error".
    • Dans le cas d'une erreur déclenchée par un événement saving/dropping, lorsqu'un objet d'erreur est renvoyé, l'erreur est toujours définie comme critique, quelle que soit la valeur de la propriété seriousError.

    Description des fonctions

    Function event touched

    Syntaxe

    {local} Function event touched($event : Object)
    {local} Function event touched <attributeName>($event : Object)
    // code

    Cet événement est déclenché chaque fois qu'une valeur est modifiée dans l'entité.

    • Si vous avez défini la fonction au niveau de l'entité (première syntaxe), elle est déclenchée pour des modifications sur n'importe quel attribut de l'entité.
    • Si vous avez défini la fonction au niveau de l'attribut (deuxième syntaxe), elle n'est déclenchée que pour les modifications sur cet attribut.

    Cet événement est déclenché dès que le moteur de 4D Server / 4D détecte une modification de la valeur de l'attribut qui peut être due aux actions suivantes :

    • en client/serveur avec le mot-clé local ou en 4D mono-utilisateur :
      • l'utilisateur saisit une valeur dans un formulaire 4D,
      • le code 4D effectue une assignation avec l'opérateur :=. L'événement est également déclenché en cas d'auto-assignation ($entity.attribute:=$entity.attribute).
    • en client/serveur sans le mot-clé local : du code 4D effectue une assignation avec l'opérateur := est exécuté sur le serveur.
    • en client/serveur sans le mot-clé local, une application Qodly ou datastore distant : l'entité est reçue sur le serveur 4D lors de l'appel d'une fonction ORDA (sur l'entité ou avec l'entité en tant que paramètre). Cela signifie que vous devrez peut-être mettre en place une fonction refresh ou preview sur l'application distante qui envoie une requête ORDA au serveur et déclenche l'événement.
    • avec le serveur REST : la valeur est reçue sur le serveur REST avec une requête REST ($method=update)

    La fonction reçoit un objet event en paramètre.

    Si cette fonction génère une erreur, elle n'arrêtera pas l'action en cours.

    note

    Cet événement est également déclenché :

    Exemple 1

    You want to uppercase all text attributes of an entity when it is updated.

        //ProductsEntity class
    Function event touched($event : Object)

    If (Value type(This[$event.attributeName])=Is text)
    This[$event.attributeName]:=Uppercase(This[$event.attributeName])
    End if

    Exemple 2

    The "touched" event is useful when it is not possible to write indexed query code in Function query() for a computed attribute.

    This is the case for example, when your query function has to compare the value of different attributes from the same entity with each other. You must use formulas in the returned ORDA query -- which triggers sequential queries.

    To fully understand this case, let's examine the following two calculated attributes:

    Function get onGoing() : Boolean
    return ((This.departureDate<=Current date) & (This.arrivalDate>=Current date))

    Function get sameDay() : Boolean
    return (This.departureDate=This.arrivalDate)

    Even though they are very similar, these functions cannot be associated with identical queries because they do not compare the same types of values. The first compares attributes to a given value, while the second compares attributes to each other.

    • For the onGoing attribute, the query function is simple to write and uses indexed attributes:
    Function query onGoing($event : Object) : Object
    var $operator : Text
    var $myQuery : Text
    var $onGoingValue : Boolean
    var $parameters : Collection
    $parameters:=New collection()

    $operator:=$event.operator
    Case of
    : (($operator="=") | ($operator="==") | ($operator="==="))
    $onGoingValue:=Bool($event.value)
    : (($operator="!=") | ($operator="!=="))
    $onGoingValue:=Not(Bool($event.value))
    Else
    return {query: ""; parameters: $parameters}
    End case

    $myQuery:=($onGoingValue) ? "departureDate <= :1 AND arrivalDate >= :1" : "departureDate > :1 OR arrivalDate < :1"
    // the ORDA query string uses indexed attributes, it will be indexed
    $parameters.push(Current date)
    return {query: $myQuery; parameters: $parameters}
    • For the sameDay attribute, the query function requires an ORDA query based on formulas and will be sequential:
    Function query sameDay($event : Object) : Text
    var $operator : Text
    var $sameDayValue : Boolean

    $operator:=$event.operator
    Case of
    : (($operator="=") | ($operator="==") | ($operator="==="))
    $sameDayValue:=Bool($event.value)
    : (($operator="!=") | ($operator="!=="))
    $sameDayValue:=Not(Bool($event.value))
    Else
    return ""
    End case

    return ($sameDayValue) ? "eval(This.departureDate = This.arrivalDate)" : "eval(This.departureDate != This.arrivalDate)"
    // the ORDA query string uses a formula, it will not be indexed

    • Using a scalar sameDay attribute updated when other attributes are "touched" will save time:
        //BookingEntity class

    Function event touched departureDate($event : Object)

    This.sameDay:=(This.departureDate = This.arrivalDate)
    //
    //
    Function event touched arrivalDate($event : Object)

    This.sameDay:=(This.departureDate = This.arrivalDate)

    Example 3 (diagram): Client/server with the local keyword:

    Example 4 (diagram): Client/server without the local keyword

    Example 5 (diagram): Qodly application

    Function event validateSave

    Syntaxe

    Function event validateSave($event : Object)
    Function event validateSave <attributeName>($event : Object)
    // code

    This event is triggered each time an entity is about to be saved.

    • if you defined the function at the entity level (first syntax), it is called for any attribute of the entity.
    • if you defined the function at the attribute level (second syntax), it is called only for this attribute. This function is not executed if the attribute has not been touched in the entity.

    La fonction reçoit un objet event en paramètre.

    This event is triggered by the following functions:

    This event is triggered before the entity is actually saved and lets you check data consistency so that you can stop the action if needed. For example, you can check in this event that "departure date" < "arrival date".

    To stop the action, the code of the function must return an error object.

    note

    It is not recommended to update the entity within this function (using This).

    Exemple

    In this example, it is not allowed to save a product with a margin lower than 50%. In case of an invalid price attribute, you return an error object and thus, stop the save action.

    // ProductsEntity class
    //
    // validateSave event at attribute level
    Function event validateSave margin($event : Object) : Object

    var $result : Object

    //The user can't create a product whose margin is < 50%
    If (This.margin<50)
    $result:={errCode: 1; message: "The validation of this product failed"; \
    extraDescription: {info: "The margin of this product ("+String(This.margin)+") is lower than 50%"}; seriousError: False}
    End if
    return $result

    Function event saving

    Syntaxe

    Function event saving($event : Object)
    Function event saving <attributeName>($event : Object)
    // code

    This event is triggered each time an entity is being saved.

    • If you defined the function at the entity level (first syntax), it is called for any attribute of the entity. The function is executed even if no attribute has been touched in the entity (e.g. in case of sending data to an external app each time a save is done).
    • If you defined the function at the attribute level (second syntax), it is called only for this attribute. The function is not executed if the attribute has not been touched in the entity.

    La fonction reçoit un objet event en paramètre.

    This event is triggered by the following functions:

    This event is triggered while the entity is actually saved. If a validateSave() event function was defined, the saving() event function is called if no error was triggered by validateSave(). For example, you can use this event to create a document on a Google Drive account.

    note

    The business logic should raise errors which can't be detected during the validateSave() events, e.g. a network error

    During the save action, 4D engine errors can be raised (index, stamp has changed, not enough space on disk).

    To stop the action, the code of the function must return an error object.

    Exemple

    When a file is saved on disk, catch errors related to disk space for example.

    // ProductsEntity class
    // saving event at attribute level
    Function event saving userManualPath($event : Object) : Object

    var $result : Object
    var $userManualFile : 4D.File
    var $fileCreated : Boolean

    If (This.userManualPath#"")
    $userManualFile:=File(This.userManualPath)

    // The user manual document file is created on the disk
    // This may fail if no more space is available
    Try
    $fileCreated:=$userManualFile.create()
    Catch
    // No more room on disk for example
    $result:={/
    errCode: 1; message: "Error during the save action for this product"; /
    extraDescription: {info: "There is no available space on disk to store the user manual"}/
    }
    End try
    End if

    return $result

    Function event afterSave

    Syntaxe

    Function event afterSave($event : Object)
    // code

    This event is triggered just after an entity is saved in the data file, when at least one attribute was modified. It is not executed if no attribute has been touched in the entity.

    This event is useful after saving data to propagate the save action outside the application or to execute administration tasks. For example, it can be used to send a confirmation email after data have been saved. Or, in case of error while saving data, it can make a rollback to restore a consistent state of data.

    La fonction reçoit un objet event en paramètre.

    • To avoid infinite loops, calling a save() on the current entity (through This) in this function is not allowed. It will raise an error.
    • Throwing an error object is not supported by this function.

    Exemple

    If an error occurred in the above saving event, the attribute value is reset accordingly in the afterSave event:

    // ProductsEntity class
    Function event afterSave($event : Object)

    If (($event.status.success=False) && ($event.status.errors=Null))
    // $event.status.errors is filled if the error comes from the validateSave event

    // The userManualPath attribute has not been properly saved
    // Its value is reset
    If ($event.savedAttributes.indexOf("userManualPath")=-1)
    This.userManualPath:=""
    This.status:="KO"
    End if

    End if

    Function event validateDrop

    Syntaxe

    Function event validateDrop($event : Object)
    Function event validateDrop <attributeName>($event : Object)
    // code

    This event is triggered each time an entity is about to be dropped.

    • If you defined the function at the entity level (first syntax), it is called for any attribute of the entity.
    • If you defined the function at the attribute level (second syntax), it is called only for this attribute.

    La fonction reçoit un objet event en paramètre.

    This event is triggered by the following features:

    This event is triggered before the entity is actually dropped, allowing you to check data consistency and if necessary, to stop the drop action.

    To stop the action, the code of the function must return an error object.

    Exemple

    In this example, it is not allowed to drop a product that is not labelled "TO DELETE". In this case, you return an error object and thus, stop the drop action.

    // ProductsEntity class

    Function event validateDrop status($event : Object) : Object

    var $result : Object

    // Products must be marked as TO DELETE to be dropped
    If (This.status#"TO DELETE")
    $result:={errCode: 1; message: "You can't drop this product"; \
    extraDescription: {info: "This product must be marked as To Delete"}; seriousError: False}
    End if

    return $result

    Function event dropping

    Syntaxe

    Function event dropping($event : Object)
    Function event dropping <attributeName>($event : Object)
    // code

    This event is triggered each time an entity is being dropped.

    • If you defined the function at the entity level (first syntax), it is called for any attribute of the entity.
    • If you defined the function at the attribute level (second syntax), it is called only for this attribute.

    La fonction reçoit un objet event en paramètre.

    This event is triggered by the following features:

    This event is triggered while the entity is actually dropped. If a validateDrop() event function was defined, the dropping() event function is called if no error was triggered by validateDrop().

    note

    The business logic should raise errors which cannot be detected during the validateDrop() events, e.g. a network error.

    To stop the action, the code of the function must return an error object.

    Exemple

    Here is an example of dropping event at entity level:

    // ProductsEntity class
    Function event dropping($event : Object) : Object

    var $result : Object
    var $userManualFile : 4D.File

    $userManualFile:=File(This.userManualPath)

    // When dropping a product, its user manual is also deleted on the disk
    // This action may fail
    Try
    If ($userManualFile.exists)
    $userManualFile.delete()
    End if
    Catch
    // Dropping the user manual failed
    $result:={errCode: 1; message: "Drop failed"; extraDescription: {info: "The user manual can't be dropped"}}
    End try

    return $result

    Function event afterDrop

    Syntaxe

    Function event afterDrop($event : Object)
    // code

    This event is triggered just after an entity is dropped.

    This event is useful after dropping data to propagate the drop action outside the application or to execute administration tasks. For example, it can be used to send a cancellation email after data have been dropped. Or, in case of error while dropping data, it can log an information for the administrator to check data consistency.

    La fonction reçoit un objet event en paramètre.

    • To avoid infinite loops, calling a drop() on the current entity (through This) in this function is not allowed. It will raise an error.
    • Throwing an error object is not supported by this function.
    note

    The dropped entity is referenced by This and still exists in memory.

    Exemple

    If the drop action failed, then the product must be checked manually:

    Function event afterDrop($event : Object)

    var $status : Object

    If (($event.status.success=False) && ($event.status.errors=Null))
    //$event.status.errors is filled
    //if the error comes from the validateDrop event
    This.status:="Check this product - Drop action failed"
    $status:=This.save()
    End if