Aller au contenu principal
Version: 19

Classes du modèle de données

ORDA vous permet de créer des fonctions de classe de haut niveau au-dessus du modèle de données. Cela vous permet d'écrire du code orienté métier et de le «publier» comme une API. Le datastore, les dataclasses, les entity selections et les entités sont tous disponibles en tant qu'objets de classe pouvant contenir des fonctions.

Par exemple, vous pouvez créer une fonction getNextWithHigherSalary() dans la classe EmployeeEntity pour retourner les employés ayant un salaire supérieur à celui qui est sélectionné. Il serait aussi simple à appeler que :

$nextHigh:=ds.Employee.get(1).getNextWithHigherSalary()

Les développeurs peuvent utiliser ces fonctions non seulement dans des datastores locaux, mais aussi dans des architectures client/serveur et distantes :

 //$cityManager est la référence d'un datastore distant
Form.comp.city:=$cityManager.City.getCityName(Form.comp.zipcode)

Grâce à cette fonctionnalité, toute la logique métier de votre application 4D peut être stockée comme une couche indépendante afin d'être facilement maintenue ou réutilisée avec un niveau de sécurité élevé :

  • Elle vous permet de «masquer» la complexité globale de la structure physique sous-jacente et d'exposer uniquement des fonctions compréhensibles et prêtes à l'emploi.

  • Si la structure physique évolue, il vous suffit d'adapter le code de la fonction et les applications clientes continueront de les appeler de manière transparente.

  • Par défaut, toutes vos fonctions de classe de modèle de données ne sont pas exposées aux applications distantes et ne peuvent pas être appelées à partir de requêtes REST. Vous devez déclarer explicitement chaque fonction publique avec le mot-clé exposed.

De plus, 4D crée préalablement et automatiquement les classes pour chaque objet de modèle de données disponible.

Architecture

ORDA fournit des classes génériques exposées via le class store 4D, ainsi que des classes utilisateurs (étendant les classes génériques) exposées dans le class store cs :

Toutes les classes de modèle de données ORDA sont exposées en tant que propriétés du class store cs. Les classes ORDA suivantes sont disponibles :

ClassNom de l'exempleInstanciée par
cs.DataStorecs.DataStorecommande ds
cs.DataClassNamecs.EmployeedataStore.DataClassName, dataStore["DataClassName"]
cs.DataClassNameEntitycs.EmployeeEntitydataClass.get(), dataClass.new(), entitySelection.first(), entitySelection.last(), entity.previous(), entity.next(), entity.first(), entity.last(), entity.clone()
cs.DataClassNameSelectioncs.EmployeeSelectiondataClass.query(), entitySelection.query(), dataClass.all(), dataClass.fromCollection(), dataClass.newSelection(), entitySelection.drop(), entity.getSelection(), entitySelection.and(), entitySelection.minus(), entitySelection.or(), entitySelection.orderBy(), entitySelection.orderByFormula(), entitySelection.slice(), Create entity selection

Les classes utilisateur ORDA sont stockées sous forme de fichiers de classe standard (.4dm) dans le sous-dossier Classes du projet (voir ci-dessous).

De plus, les instances d'objet de classes utilisateurs du modèles de données ORDA bénéficient des propriétés et fonctions de leurs parents:

Description de la classe

Historique
ReleaseModifications
18 R5Les fonctions des classes du modèle de données ne sont pas exposées par défaut en REST. Nouveaux mots-clés exposed et local.

Classe DataStore

Une base de données 4D expose sa propre classe DataStore dans le class store cs.

  • Extends: 4D.DataStoreImplementation
  • Nom de classe : cs.DataStore

Vous pouvez créer des fonctions dans la classe DataStore qui seront disponibles via l'objet ds.

Exemple

// cs.DataStore class

Class extends DataStoreImplementation

Function getDesc
$0:="Database exposing employees and their companies"

Cette foncton peut alors être appelée :

$desc:=ds.getDesc() //"Database exposing..."

Classe DataClass

Chaque table exposée avec ORDA affiche une classe DataClass dans le class store cs.

  • Extends : 4D.DataClass
  • **Nom de classe **: cs.DataClassName (où DataClassName est le nom de la table)
  • **Exemple ** : cs.Employee

Exemple

// cs.Company class


Class extends DataClass

// Returns companies whose revenue is over the average
// Returns an entity selection related to the Company DataClass

Function GetBestOnes()
$sel:=This.query("revenues >= :1";This.all().average("revenues"));
$0:=$sel

Vous pouvez ensuite obtenir une sélection d'entité des "meilleures" entreprises en exécutant le code suivant :

 var $best : cs.CompanySelection
$best:=ds.Company.GetBestOnes()

Exemple avec un datastore distant

Le catalogue City suivant est exposé dans un datastore distant (vue partielle) :

La classe City Class fournit une API :

// cs.City class

Class extends DataClass

Function getCityName()
var $1; $zipcode : Integer
var $zip : 4D.Entity
var $0 : Text

$zipcode:=$1
$zip:=ds.ZipCode.get($zipcode)
$0:=""

If ($zip#Null)
$0:=$zip.city.name
End if

L'application cliente ouvre une session sur le datastore distant :

$cityManager:=Open datastore(New object("hostname";"127.0.0.1:8111");"CityManager")

Une application cliente peut alors utiliser l'API pour obtenir la ville correspondant au code postal (par exemple) à partir d'un formulaire :

Form.comp.city:=$cityManager.City.getCityName(Form.comp.zipcode)

Classe EntitySelection

Chaque table exposée avec ORDA affiche une classe EntitySelection dans le class store cs.

  • Extends : 4D.EntitySelection
  • Nom de classe : DataClassNameSelection (où DataClassName est le nom de la table)
  • **Exemple ** : cs.EmployeeSelection

Exemple

// cs.EmployeeSelection class


Class extends EntitySelection

//Extract the employees with a salary greater than the average from this entity selection

Function withSalaryGreaterThanAverage
C_OBJECT($0)
$0:=This.query("salary > :1";This.average("salary")).orderBy("salary")

Vous pouvez alors obtenir les employés dont le salaire est supérieur à la moyenne, dans une sélection d'entité, en exécutant le code suivant :

$moreThanAvg:=ds.Company.all().employees.withSalaryGreaterThanAverage()

Classe Entity

Chaque table exposée avec ORDA affiche une classe Entity dans le class store cs.

  • Extends : 4D.Entity
  • **Nom de classe **: DataClassNameEntity (où DataClassName est le nom de la table)
  • **Exemple ** : cs.CityEntity

Exemple

// cs.CityEntity class

Class extends Entity

Function getPopulation()
$0:=This.zips.sum("population")


Function isBigCity
C_BOOLEAN($0)
// La fonction getPopulation() peut être utilisée dans la classe
$0:=This.getPopulation()>50000

Vous pouvez ensuite appeler ce code :

var $cityManager; $city : Object

$cityManager:=Open datastore(New object("hostname";"127.0.0.1:8111");"CityManager")
$city:=$cityManager.City.getCity("Caguas")

If ($city.isBigCity())
ALERT($city.name + " is a big city")
End if

Règles spécifiques

Lors de la création ou de la modification de classes de modèles de données, vous devez veiller aux règles décrites ci-dessous :

  • Puisqu'ils sont utilisés pour définir des noms de classe DataClass automatiques dans le class store cs, les tables 4D doivent être nommées afin d'éviter tout conflit dans l'espace de nommage cs. En particulier :

    • Ne donnez pas le même nom à une table 4D et à une classe d'utilisateurs (user class). Si un tel cas se produit, le constructeur de la classe utilisateur devient inutilisable (un avertissement est retourné par le compilateur).
    • N'utilisez pas de nom réservé pour une table 4D (par exemple "DataClass").
  • Lors de la définition d'une classe, assurez-vous que l'instruction Class extends correspond exactement au nom de la classe parente (sensible à la casse). Par exemple, Class extends EntitySelection pour une classe de sélection d'entité.

  • Vous ne pouvez pas instancier un objet de classe de modèle de données avec le mot clé new() (une erreur est retournée). Vous devez utiliser une des méthodes standard listées dans la colonne Instanciée par du tableau des classes ORDA.

  • Vous ne pouvez pas remplacer une fonction de classe ORDA native du class store 4D par une fonction de classe utilisateur de modèle de données.

Fonctions exposées et non exposées

Pour des raisons de sécurité, toutes vos fonctions de classe de modèle de données sont non-exposées (not exposed) par défaut aux requêtes distantes (c'est-à-dire qu'elles sont privées).

Les requêtes à distance incluent :

  • Les requêtes envoyées par des applications 4D distantes connectées via Open datastore
  • Les requêtes REST

Les requêtes client/serveur 4D standard ne sont pas impactées. Les fonctions de classe de modèle de données sont toujours disponibles dans cette architecture.

Une fonction qui n'est pas exposée n'est pas disponible sur les applications distantes et ne peut être appelée sur aucune instance d'objet à partir d'une requête REST. Si une application distante tente d'accéder à une fonction non exposée, l'erreur «-10729 - Méthode membre inconnue» est retournée.

Pour permettre à une fonction de classe de modèle de données d'être appelée par une requête distante, vous devez la déclarer explicitement à l'aide du mot-clé exposed. La syntaxe formelle est la suivante :

// déclarer une fonction exposée
exposed Function <functionName>

Le mot-clé exposed ne peut être utilisé qu'avec les fonctions de classe du modèle de données. S'il est utilisé avec une fonction de classe utilisateur standard, il est ignoré et une erreur est retournée par le compilateur.

Exemple

Vous voulez qu'une fonction exposée utilise une fonction privée dans une classe dataclass :

Class extends DataClass

//Fonction publique
exposed Function registerNewStudent($student : Object) -> $status : Object

var $entity : cs.StudentsEntity

$entity:=ds.Students.new()
$entity.fromObject($student)
$entity.school:=This.query("name=:1"; $student.schoolName).first()
$entity.serialNumber:=This.computeSerialNumber()
$status:=$entity.save()

//fonction (privée) non exposée
Function computeIDNumber()-> $id : Integer
//calculer un nouveau numéro d'ID
$id:=...

Lorsque le code est appelé :

var $remoteDS; $student; $status : Object
var $id : Integer

$remoteDS:=Open datastore(New object("hostname"; "127.0.0.1:8044"); "students")
$student:=New object("firstname"; "Mary"; "lastname"; "Smith"; "schoolName"; "Math school")

$status:=$remoteDS.Schools.registerNewStudent($student) // OK
$id:=$remoteDS.Schools.computeIDNumber() // Erreur "Unknown member method"

Fonctions locales

Par défaut dans l'architecture client/serveur, les fonctions de modèle de données ORDA sont exécutées sur le serveur. Cela garantit généralement les meilleures performances puisque seuls la requête de fonction et le résultat sont envoyés sur le réseau.

Cependant, il peut arriver qu'une fonction soit entièrement exécutable côté client (par exemple, lorsqu'elle traite des données qui se trouvent déjà dans le cache local). Dans ce cas, vous pouvez économiser des requêtes au serveur et ainsi améliorer les performances de l'application en saisissant le mot-clé local. La syntaxe formelle est la suivante :

// déclarer une fonction à exécuter localement en client/serveur 
local Function <functionName>

Avec ce mot-clé, la fonction sera toujours exécutée côté client.

Le mot-clé local ne peut être utilisé qu'avec les fonctions de classe du modèle de données. S'il est utilisé avec une fonction de classe utilisateur standard, il est ignoré et une erreur est retournée par le compilateur.

A noter que la fonction sera exécutée avec succès même si elle nécessite d'accéder au serveur (par exemple si le cache ORDA est expiré). Toutefois, il est fortement recommandé de s'assurer que la fonction locale n'accède pas aux données sur le serveur, sinon l'exécution locale pourrait n'apporter aucun avantage en termes de performances. Une fonction locale qui génère de nombreuses requêtes au serveur est moins efficace qu'une fonction exécutée sur le serveur qui ne retournerait que les valeurs résultantes. Prenons l'exemple suivant, avec une fonction sur l'entité Schools :

// Get the youngest students  
// Inappropriate use of local keyword
local Function getYoungest
var $0 : Object
$0:=This.students.query("birthDate >= :1"; !2000-01-01!).orderBy("birthDate desc").slice(0; 5)
  • sans le mot clé local, le résultat est donné en une seule requête
  • avec le mot-clé local, 4 requêtes sont nécessaires : une pour obtenir les élèves de l'entité Schools, une pour la query(), une pour le orderBy() et une pour la slice(). Dans cet exemple, l'utilisation du mot-clé local est inappropriée.

Exemples

Calcul de l'âge

Considérons une entité avec un attribut birthDate. Nous souhaitons définir une fonction age() qui serait appelée dans une list box. Cette fonction peut être exécutée sur le client, ce qui évite de déclencher une requête au serveur pour chaque ligne de la list box.

Dans la classe StudentsEntity :

Class extends Entity

local Function age() -> $age: Variant

If (This.birthDate#!00-00-00!)
$age:=Year of(Current date)-Year of(This.birthDate)
Else
$age:=Null
End if

Vérification des attributs

Nous souhaitons vérifier la cohérence des attributs d'une entité chargée sur le client et mise à jour par l'utilisateur, avant de demander au serveur de les enregistrer.

Sur la classe StudentsEntity, la fonction locale checkData() vérifie l'âge de l'étudiant :

Class extends Entity

local Function checkData() -> $status : Object

$status:=New object("success"; True)
Case of
: (This.age()=Null)
$status.success:=False
$status.statusText:="The birthdate is missing"

:((This.age() <15) | (This.age()>30) )
$status.success:=False
$status.statusText:="The student must be between 15 and 30 - This one is "+String(This.age())
End case

Code d'appel :

var $status : Object

//Form.student est chargé avec tous ses a attributs et mis à jour sur un Form
$status:=Form.student.checkData()
If ($status.success)
$status:=Form.student.save() // appelle le serveur
End if

Prise en charge dans les projets 4D

Fichiers de classe (class files)

Une classe utilisateur ORDA de modèle de données est définie en ajoutant, au même emplacement que les fichiers de classe usuels (c'est-à-dire dans le dossier /Sources/Classes du dossier projet), un fichier .4dm avec le nom de la classe. Par exemple, une classe d'entité pour la dataclass Utilities sera définie via un fichier UtilitiesEntity.4dm.

Créer des classes

4D crée préalablement et automatiquement des classes vides en mémoire pour chaque objet de modèle de données disponible.

Par défaut, les classes ORDA vides ne sont pas affichées dans l'Explorateur. Vous devez les afficher en sélectionnant Afficher toutes les dataclasses dans le menu d'options de l'Explorateur :

Les classes utilisateurs ORDA ont une icône différente des autres classes. Les classes vides sont grisées :

Pour créer un fichier de classe ORDA, il vous suffit de double-cliquer sur la classe prédéfinie correspondante dans l'Explorateur. 4D crée le fichier de classe et ajoute le code extends. Par exemple, pour une classe Entity :

Class extends Entity

Une fois qu'une classe est définie, son nom n'est plus grisé dans l'Explorateur.

Modifier des classes

Pour ouvrir une classe ORDA définie dans l'éditeur de méthode 4D, sélectionnez ou double-cliquez sur un nom de classe ORDA et utilisez Editer... dans le menu contextuel/menu d'options de la fenêtre d'Explorateur :

Pour les classes ORDA basées sur le datastore local (ds), vous pouvez accéder directement au code de la classe depuis la fenêtre de 4D Structure :

Éditeur de méthode

Dans l'éditeur de méthode de 4D, les variables saisies comme une classe ORDA bénéficient automatiquement des fonctionnalités d'auto-complétion. Exemple avec une variable de classe Entity :