Saltar para o conteúdo principal
Versão: 20 R5 BETA

Classes de modelo de dados

O ORDA permite-lhe criar funções de classe de alto nível acima do modelo de dados. Isto permite-lhe escrever código orientado para o negócio e "publicá-lo" tal como uma API. Datastore, classes de dados, seleções de entidades e entidades estão todos disponíveis como objetos de classe que podem conter funções.

For example, you could create a getNextWithHigherSalary() function in the EmployeeEntity class to return employees with a salary higher than the selected one. Seria tão simples como chamar:

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

Os desenvolvedores podem não só utilizar estas funções em datastores locais, mas também em arquiteturas cliente/servidor e remotas:

 //$cityManager é a referência de um datastore remoto
Form.comp.city:=$cityManager.City.getCityName(Form.comp.zipcode)

Graças a esta caraterística, toda a lógica comercial da sua aplicação 4D pode ser armazenada como uma camada independente para que possa ser facilmente mantida e reutilizada com um alto nível de segurança:

  • É possível "esconder" a complexidade global da estrutura física subjacente e expor apenas funções compreensíveis e prontas a utilizar.

  • Se a estrutura física evoluir, pode simplesmente adaptar o código da função e as aplicações cliente continuarão a chamá-las de forma transparente.

  • By default, all of your data model class functions (including computed attribute functions) and alias attributes are not exposed to remote applications and cannot be called from REST requests. You must explicitly declare each public function and alias with the exposed keyword.

In addition, 4D automatically pre-creates the classes for each available data model object.

Arquitetura

ORDA provides generic classes exposed through the 4D class store, as well as user classes (extending generic classes) exposed in the cs class store:

All ORDA data model classes are exposed as properties of the cs class store. Estão disponíveis as seguintes classes ORDA:

ClassNome do exemploInstanciado por
cs. DataStorecs. DataStoreds command
cs.DataClassNamecs. EmployeedataStore.DataClassName, dataStore["DataClassName"]
cs._DataClassName_Entitycs. EmployeeEntitydataClass.get(), dataClass.new(), entitySelection.first(), entitySelection.last(), entity.previous(), entity.next(), entity.first(), entity.last(), entity.clone()
cs._DataClassName_Selectioncs. 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

ORDA user classes are stored as regular class files (.4dm) in the Classes subfolder of the project (see below).

Além disso, as instâncias de objetos das classes de usuárioes do modelo de dados ORDA beneficiam das propriedades e funções dos seus pais:

Descrição da classe

História
ReleaseMudanças
19 R4Atributos alias na Entity Class
19 R3Atributos calculados en la Entity Class
18 R5As funções de classe do modelo de dados não são expostas ao REST por defeito. New exposed and local keywords.

Classe DataStore

A 4D database exposes its own DataStore class in the cs class store.

  • Extends: 4D.DataStoreImplementation
  • Class name: cs.DataStore

You can create functions in the DataStore class that will be available through the ds object.

Exemplo

// cs. DataStore class Class extends DataStoreImplementation Function getDesc
$0:="Database exposing employees and their companies"

Esta função pode então ser chamada:

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

Classe DataClass

Each table exposed with ORDA offers a DataClass class in the cs class store.

  • Extends: 4D.DataClass
  • Class name: cs.DataClassName (where DataClassName is the table name)
  • Example name: cs.Employee

Exemplo

// 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

Em seguida, pode obter uma seleção de entidades das "melhores" empresas através da execução:

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

Computed attributes are defined in the Entity Class.

Exemplo com um datastore remoto

The following City catalog is exposed in a remote datastore (partial view):

The City Class provides an 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

A aplicação cliente abre uma sessão no datastore remoto:

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

Em seguida, uma aplicação cliente pode utilizar a API para obter a cidade correspondente a um código postal (por exemplo) a partir de um formulário:

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

Classe EntitySelection

Each table exposed with ORDA offers an EntitySelection class in the cs class store.

  • Extends: 4D.EntitySelection
  • Class name: DataClassName_Selection (where _DataClassName is the table name)
  • Example name: cs.EmployeeSelection

Exemplo

// cs.EmployeeSelection class


Class extends EntitySelection

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

Function withSalaryGreaterThanAverage() : cs.EmployeeSelection
return This.query("salary > :1";This.average("salary")).orderBy("salary")

Em seguida, é possível obter empregados com um salário superior à média em qualquer seleção de entidade através da execução:

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

Entity Class

Each table exposed with ORDA offers an Entity class in the cs class store.

  • Extends: 4D.Entity
  • Class name: DataClassName_Entity (where _DataClassName is the table name)
  • Example name: cs.CityEntity

Atributos calculados

Entity classes allow you to define computed attributes using specific keywords:

  • Function get attributeName
  • Function set attributeName
  • Function query attributeName
  • Function orderBy attributeName

For information, please refer to the Computed attributes section.

Atributos de Alias

Entity classes allow you to define alias attributes, usually over related attributes, using the Alias keyword:

Alias attributeName targetPath

For information, please refer to the Alias attributes section.

Exemplo

// cs.CityEntity class

Class extends Entity

Function getPopulation() : Integer
return This.zips.sum("population")


Function isBigCity(): Boolean
// The getPopulation() function is usable inside the class
return This.getPopulation()>50000

Em seguida, pode chamar este código:

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

Regras específicas

Ao criar ou editar classes de modelo de dados, é necessário preste atenção às seguintes regras:

  • Since they are used to define automatic DataClass class names in the cs class store, 4D tables must be named in order to avoid any conflict in the cs namespace. Em particular:

    • Do not give the same name to a 4D table and to a user class name. Se isso acontecer, o construtor da classe de utilizador torna-se inutilizável (o compilador emite um aviso).
    • Não use um nome reservado para uma tabela 4D (por exemplo, "DataClass").
  • When defining a class, make sure the Class extends statement exactly matches the parent class name (remember that they're case sensitive). For example, Class extends EntitySelection for an entity selection class.

  • You cannot instantiate a data model class object with the new() keyword (an error is returned). You must use a regular method as listed in the Instantiated by column of the ORDA class table.

  • You cannot override a native ORDA class function from the 4D class store with a data model user class function.

Execução preventiva

Quando compiladas, as funções da classe do modelo de dados são executadas:

  • in preemptive or cooperative processes (depending on the calling process) in single-user applications,
  • in preemptive processes in client/server applications (except if the local keyword is used, in which case it depends on the calling process like in single-user).

Se o seu projeto foi concebido para ser executado em cliente/servidor, certifique-se de que o código da função da classe do modelo de dados é thread-safe. Se o código thread-unsafe for chamado, será lançado um erro em tempo de execução (nenhum erro será lançado em tempo de compilação, uma vez que a execução cooperativa é suportada em aplicações de utilizador único).

Atributos calculados

Visão Geral

Um atributo computado é um atributo de classe de dados com um tipo de dados que oculta um cálculo. Standard 4D classes implement the concept of computed properties with get (getter) and set (setter) accessor functions. ORDA dataclass attributes benefit from this feature and extend it with two additional functions: query and orderBy.

At the very minimum, a computed attribute requires a get function that describes how its value will be calculated. When a getter function is supplied for an attribute, 4D does not create the underlying storage space in the datastore but instead substitutes the function's code each time the attribute is accessed. Se o atributo não for acessado, o código nunca é executado.

A computed attribute can also implement a set function, which executes whenever a value is assigned to the attribute. The setter function describes what to do with the assigned value, usually redirecting it to one or more storage attributes or in some cases other entities.

Just like storage attributes, computed attributes may be included in queries. Como padrão, quando um atributo calculado for utilizado numa consulta ORDA, o atributo é calculado uma vez por entidade examinada. Em alguns casos, isto é suficiente. However for better performance, especially in client/server, computed attributes can implement a query function that relies on actual dataclass attributes and benefits from their indexes.

Similarly, computed attributes can be included in sorts. Quando um atributo calculado é utilizado numa ordenação ORDA, o atributo é calculado uma vez por entidade examinada. Just like in queries, computed attributes can implement an orderBy function that substitutes other attributes during the sort, thus increasing performance.

Como definir atributos computados

You create a computed attribute by defining a get accessor in the entity class of the dataclass. O atributo computado estará automaticamente disponível nos atributos da classe de dados e nos atributos da entidade.

Other computed attribute functions (set, query, and orderBy) can also be defined in the entity class. São facultativos.

Within computed attribute functions, This designates the entity. Computed attributes can be used and handled as any dataclass attribute, i.e. they will be processed by entity class or entity selection class functions.

ORDA computed attributes are not exposed by default. You expose a computed attribute by adding the exposed keyword to the get function definition.

get and set functions can have the local property to optimize client/server processing.

Function get <attributeName>

Sintaxe

{local} {exposed} Function get <attributeName>({$event : Object}) -> $result : type
// code

The getter function is mandatory to declare the attributeName computed attribute. Whenever the attributeName is accessed, 4D evaluates the Function get code and returns the $result value.

Um atributo calculado pode utilizar o valor de outro(s) atributo(s) calculado(s). As chamadas recursivas geram erros.

The getter function defines the data type of the computed attribute thanks to the $result parameter. São permitidos os seguintes tipos de resultados:

  • Scalar (text, boolean, date, time, number)
  • Object
  • Imagem
  • BLOB
  • Entity (i.e. cs. EmployeeEntity)
  • Entity selection (i.e. cs. EmployeeSelection)

The $event parameter contains the following properties:

PropriedadeTipoDescrição
attributeNameTextNome do atributo computado
dataClassNameTextNome do dataclass
kindText"get"
resultDiferente deOpcional. Adicione esta propriedade com o valor Null se pretender que um atributo escalar devolva Null

Exemplos

  • fullName computed attribute:
Function get fullName($event : Object)-> $fullName : Text

Case of
: (This.firstName=Null) & (This.lastName=Null)
$event.result:=Null //use result to return Null
: (This.firstName=Null)
$fullName:=This.lastName
: (This.lastName=Null)
$fullName:=This.firstName
Else
$fullName:=This.firstName+" "+This.lastName
End case
  • Um atributo calculado pode ser baseado num atributo relacionado com uma entidade:
Function get bigBoss($event : Object)-> $result: cs.EmployeeEntity
$result:=This.manager.manager

  • Um atributo calculado pode ser baseado num atributo relacionado com a seleção de uma entidade:
Function get coWorkers($event : Object)-> $result: cs.EmployeeSelection
If (This.manager.manager=Null)
$result:=ds.Employee.newSelection()
Else
$result:=This.manager.directReports.minus(this)
End if

Function set <attributeName>

Sintaxe


{local} Function set <attributeName>($value : type {; $event : Object})
// code

The setter function executes whenever a value is assigned to the attribute. Esta função processa normalmente o(s) valor(es) de entrada e o resultado é enviado entre um ou mais atributos.

The $value parameter receives the value assigned to the attribute.

The $event parameter contains the following properties:

PropriedadeTipoDescrição
attributeNameTextNome do atributo computado
dataClassNameTextNome do dataclass
kindText"set"
valueDiferente deValor a tratar pelo atributo calculado

Exemplo

Function set fullName($value : Text; $event : Object)
var $p : Integer
$p:=Position(" "; $value)
This.firstname:=Substring($value; 1; $p-1) // "" if $p<0
This.lastname:=Substring($value; $p+1)

Function query <attributeName>

Sintaxe

Function query <attributeName>($event : Object)
Function query <attributeName>($event : Object) -> $result : Text
Function query <attributeName>($event : Object) -> $result : Object
// code

Esta função suporta três sintaxes:

  • With the first syntax, you handle the whole query through the $event.result object property.
  • With the second and third syntaxes, the function returns a value in $result:
    • If $result is a Text, it must be a valid query string
    • If $result is an Object, it must contain two properties: | Propriedade | Tipo | Descrição | | ---------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------- | | $result.query | Text | Cadeia de consulta válida com marcadores de posição (:1, :2, etc.) | | $result.parameters | Collection | valores para marcadores |

The query function executes whenever a query using the computed attribute is launched. É útil personalizar e otimizar as consultas com base em atributos indexados. When the query function is not implemented for a computed attribute, the search is always sequential (based upon the evaluation of all values using the get <AttributeName> function).

The following features are not supported:

  • calling a query function on computed attributes of type Entity or Entity selection,
  • using the order by keyword in the resulting query string.

The $event parameter contains the following properties:

PropriedadeTipoDescrição
attributeNameTextNome do atributo computado
dataClassNameTextNome do dataclass
kindText"query"
valueDiferente deValor a tratar pelo atributo calculado
operatorTextQuery operator (see also the query class function). Possible values:
  • == (equal to, @ is wildcard)
  • === (equal to, @ is not wildcard)
  • != (not equal to, @ is wildcard)
  • !== (not equal to, @ is not wildcard)
  • < (less than)
  • <= (less than or equal to)
  • > (greater than)
  • >= (greater than or equal to)
  • IN (included in)
  • % (contains keyword)
  • resultDiferente deValor a tratar pelo atributo calculado. Pass Null in this property if you want to let 4D execute the default query (always sequential for computed attributes).

    If the function returns a value in $result and another value is assigned to the $event.result property, the priority is given to $event.result.

    Exemplos

    • Query on the fullName computed attribute.
    Function query fullName($event : Object)->$result : Object

    var $fullname; $firstname; $lastname; $query : Text
    var $operator : Text
    var $p : Integer
    var $parameters : Collection

    $operator:=$event.operator
    $fullname:=$event.value

    $p:=Position(" "; $fullname)
    If ($p>0)
    $firstname:=Substring($fullname; 1; $p-1)+"@"
    $lastname:=Substring($fullname; $p+1)+"@"
    $parameters:=New collection($firstname; $lastname) // two items collection
    Else
    $fullname:=$fullname+"@"
    $parameters:=New collection($fullname) // single item collection
    End if

    Case of
    : ($operator="==") | ($operator="===")
    If ($p>0)
    $query:="(firstName = :1 and lastName = :2) or (firstName = :2 and lastName = :1)"
    Else
    $query:="firstName = :1 or lastName = :1"
    End if
    : ($operator="!=")
    If ($p>0)
    $query:="firstName != :1 and lastName != :2 and firstName != :2 and lastName != :1"
    Else
    $query:="firstName != :1 and lastName != :1"
    End if
    End case

    $result:=New object("query"; $query; "parameters"; $parameters)

    Keep in mind that using placeholders in queries based upon user text input is recommended for security reasons (see query() description).

    Código de chamada, por exemplo:

    $emps:=ds. Employee.query("fullName = :1"; "Flora Pionsin")
    • This function handles queries on the age computed attribute and returns an object with parameters:
    Function query age($event : Object)->$result : Object

    var $operator : Text
    var $age : Integer
    var $_ages : Collection

    $operator:=$event.operator

    $age:=Num($event.value) // integer
    $d1:=Add to date(Current date; -$age-1; 0; 0)
    $d2:=Add to date($d1; 1; 0; 0)
    $parameters:=New collection($d1; $d2)

    Case of

    : ($operator="==")
    $query:="birthday > :1 and birthday <= :2" // after d1 and before or egal d2

    : ($operator="===")

    $query:="birthday = :2" // d2 = second calculated date (= birthday date)

    : ($operator=">=")
    $query:="birthday <= :2"

    //... other operators


    End case


    If (Undefined($event.result))
    $result:=New object
    $result.query:=$query
    $result.parameters:=$parameters
    End if

    Código de chamada, por exemplo:

    // people aged between 20 and 21 years (-1 day)
    $twenty:=people.query("age = 20") // calls the "==" case

    // people aged 20 years today
    $twentyToday:=people.query("age === 20") // equivalent to people.query("age is 20")

    Function orderBy <attributeName>

    Sintaxe

    Function orderBy <attributeName>($event : Object)
    Function orderBy <attributeName>($event : Object)-> $result : Text

    // code

    The orderBy function executes whenever the computed attribute needs to be ordered. Permite ordenar o atributo calculado. For example, you can sort fullName on first names then last names, or conversely. When the orderBy function is not implemented for a computed attribute, the sort is always sequential (based upon the evaluation of all values using the get <AttributeName> function).

    Calling an orderBy function on computed attributes of type Entity class or Entity selection class is not supported.

    The $event parameter contains the following properties:

    PropriedadeTipoDescrição
    attributeNameTextNome do atributo computado
    dataClassNameTextNome do dataclass
    kindText"orderBy"
    valueDiferente deValor a tratar pelo atributo calculado
    operatorText"desc" ou "asc" (padrão)
    descendingParâmetrostrue for descending order, false for ascending order
    resultDiferente deValor a tratar pelo atributo calculado. Pass Null if you want to let 4D execute the default sort.

    You can use either the operator or the descending property. É essencialmente uma questão de estilo de programação (ver exemplos).

    You can return the orderBy string either in the $event.result object property or in the $result function result. If the function returns a value in $result and another value is assigned to the $event.result property, the priority is given to $event.result.

    Exemplo

    É possível escrever código condicional:

    Function orderBy fullName($event : Object)-> $result : Text
    If ($event.descending=True)
    $result:="firstName desc, lastName desc"
    Else
    $result:="firstName, lastName"
    End if

    Também pode escrever código compacto:

    Function orderBy fullName($event : Object)-> $result : Text
    $result:="firstName "+$event.operator+", "lastName "+$event.operator

    O código condicional é necessário em alguns casos:

    Function orderBy age($event : Object)-> $result : Text

    If ($event.descending=True)
    $result:="birthday asc"
    Else
    $result:="birthday desc"
    End if

    Atributos de Alias

    Visão Geral

    An alias attribute is built above another attribute of the data model, named target attribute. O atributo de destino pode pertencer a uma classe de dados relacionada (disponível através de qualquer número de níveis de relação) ou à mesma classe de dados. Um atributo de alias não armazena dados, mas sim o caminho para o seu atributo de destino. É possível definir tantos atributos de alias quanto se pretenda numa classe de dados.

    Os atributos de alias (pseudônimo) são particularmente úteis para tratar relações N para N. Proporcionam maior legibilidade e simplicidade no código e nas consultas, permitindo basear-se em conceitos comerciais em vez de pormenores de implementação.

    Como definir atributos alias

    You create an alias attribute in a dataclass by using the Alias keyword in the entity class of the dataclass.

    Alias <attributeName> <targetPath>

    Sintaxe

    {exposed} Alias <attributeName> <targetPath>

    attributeName must comply with standard rules for property names.

    targetPath is an attribute path containing one or more levels, such as "employee.company.name". If the target attribute belongs to the same dataclass, targetPath is the attribute name.

    Um alias pode ser utilizado como parte de um caminho de outro alias.

    A computed attribute can be used in an alias path, but only as the last level of the path, otherwise, an error is returned. Por exemplo, se "fullName" for um atributo computado, é válido um alias com o caminho "employee.fullName".

    ORDA alias attributes are not exposed by default. You must add the exposed keyword before the Alias keyword if you want the alias to be available to remote requests.

    Utilizar atributos alias

    Os atributos de alias são apenas de leitura (exceto quando baseados num atributo escalar da mesma classe de dados, ver o último exemplo abaixo). Podem ser utilizados em vez do caminho do atributo de destino em funções de classe como:

    Function
    dataClass.query(), entitySelection.query()
    entity.toObject()
    entitySelection.toCollection()
    entitySelection.extract()
    entitySelection.orderBy()
    entitySelection.orderByFormula()
    entitySelection.average()
    entitySelection.count()
    entitySelection.distinct()
    entitySelection.sum()
    entitySelection.min()
    entitySelection.max()
    entity.diff()
    entity.touchedAttributes()

    Tenha em atenção que os atributos alias são calculados no servidor. Em configurações remotas, a atualização de atributos de alias em entidades requer que as entidades sejam recarregadas a partir do servidor.

    Propriedades alias

    Alias attribute kind is "alias".

    An alias attribute inherits its data type property from the target attribute:

    • if the target attribute kind is "storage", the alias data type is of the same type,
    • if the target attribute kind is "relatedEntity" or "relatedEntities", the alias data type is of the 4D.Entity or 4D.EntitySelection type ("_classname_Entity" or "_classname_Selection").

    Alias attributes based upon relations have a specific path property, containing the path of their target attributes. Alias attributes based upon attributes of the same dataclass have the same properties as their target attributes (and no path property).

    Exemplos

    Considerando o seguinte modelo:

    Na classe de dados Teacher, um atributo alias devolve todos os alunos de um professor:

    // cs.TeacherEntity class

    Class extends Entity

    Alias students courses.student //relatedEntities

    Na classe de dados Student, um atributo alias devolve todos os professores de um aluno:

    // cs.StudentEntity class

    Class extends Entity

    Alias teachers courses.teacher //relatedEntities

    Na classe de dados Curso:

    • um atributo alias devolve outra etiqueta para o atributo "name".
    • um atributo alias devolve o nome do professor
    • um atributo alias devolve o nome do aluno
    // cs.CourseEntity class

    Class extends Entity

    Exposed Alias courseName name //scalar
    Exposed Alias teacherName teacher.name //scalar value
    Exposed Alias studentName student.name //scalar value

    Em seguida, é possível executar as seguintes consultas:

    // Find course named "Archaeology"
    ds. Course.query("courseName = :1";"Archaeology")

    // Find courses given by the professor Smith
    ds. Course.query("teacherName = :1";"Smith")

    // Find courses where Student "Martin" assists
    ds. Course.query("studentName = :1";"Martin")

    // Find students who have M. Smith as teacher
    ds. Student.query("teachers.name = :1";"Smith")

    // Find teachers who have M. Martin as Student
    ds. Teacher.query("students.name = :1";"Martin")
    // Note that this very simple query string processes a complex
    // query including a double join, as you can see in the queryPlan:
    // "Join on Table : Course : Teacher.ID = Course.teacherID,
    // subquery:[ Join on Table : Student : Course.studentID = Student.ID,
    // subquery:[ Student.name === Martin]]"

    You can also edit the value of the courseName alias:

    // Rename a course using its alias attribute
    $arch:=ds. Course.query("courseName = :1";"Archaeology")
    $arch.courseName:="Archaeology II"
    $arch.save() //courseName and name are "Archaeology II"

    Funções expostas vs não expostas

    For security reasons, all of your data model class functions and alias attributes are not exposed (i.e., private) by default to remote requests.

    Os pedidos remotos incluem:

    • Requests sent by remote 4D applications connected through Open datastore
    • Pedidos REST

    Os pedidos regulares de cliente/servidor 4D não são afetados. As funções de classe do modelo de dados estão sempre disponíveis nesta arquitetura.

    Uma função que não esteja exposta não está disponível em aplicações remotas e não pode ser chamada em qualquer instância de objeto a partir de um pedido REST. Se uma aplicação remota tentar aceder a uma função não exposta, é devolvido o erro "-10729 - Método de membro desconhecido".

    To allow a data model class function to be called by a remote request, you must explicitly declare it using the exposed keyword. A sintaxe formal é:

    // declare an exposed function
    exposed Function <functionName>

    The exposed keyword can only be used with Data model class functions. If used with a regular user class function, it is ignored and an error is returned by the compiler.

    Exemplo

    Se quiser que uma função exposta utilize uma função privada numa classe de dataclass:

    Class extends DataClass

    //Public function
    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()

    //Not exposed (private) function
    Function computeIDNumber()-> $id : Integer
    //compute a new ID number
    $id:=...

    Quando o código é chamado:

    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() // Error "Unknown member method"

    Funções locais

    By default in client/server architecture, ORDA data model functions are executed on the server. Normalmente, proporciona o melhor desempenho, uma vez que apenas o pedido de função e o resultado são enviados através da rede.

    No entanto, pode acontecer que uma função seja totalmente executável no lado do cliente (por exemplo, quando processa dados que já estão na cache local). In this case, you can save requests to the server and thus, enhance the application performance by inserting the local keyword. A sintaxe formal é:

    // declare a function to execute locally in client/server
    local Function <functionName>

    Com esta palavra-chave, a função será sempre executada no lado do cliente.

    The local keyword can only be used with data model class functions. If used with a regular user class function, it is ignored and an error is returned by the compiler.

    Note-se que a função funcionará mesmo que eventualmente seja necessário aceder ao servidor (por exemplo, se a cache ORDA tiver expirado). No entanto, é altamente recomendável certificar-se de que a função local não acede a dados no servidor, caso contrário a execução local não poderá trazer qualquer benefício em termos de desempenho. Uma função local que gera muitos pedidos ao servidor é menos eficiente do que uma função executada no servidor que apenas devolveria os valores resultantes. Por exemplo, considere a seguinte função na classe de entidade Escolas:

    // 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)
    • without the local keyword, the result is given using a single request
    • with the local keyword, 4 requests are necessary: one to get the Schools entity students, one for the query(), one for the orderBy(), and one for the slice(). In this example, using the local keyword is inappropriate.

    Exemplos

    Cálculo da idade

    Given an entity with a birthDate attribute, we want to define an age() function that would be called in a list box. Esta função pode ser executada no cliente, o que evita desencadear um pedido ao servidor para cada linha da caixa de listagem.

    On the StudentsEntity class:

    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

    Verificação de atributos

    Pretendemos verificar a consistência dos atributos de uma entidade carregada no cliente e actualizada pelo utilizador antes de solicitar ao servidor que os guarde.

    On the StudentsEntity class, the local checkData() function checks the Student's age:

    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

    Código de chamada:

    var $status : Object

    //Form.student is loaded with all its attributes and updated on a Form
    $status:=Form.student.checkData()
    If ($status.success)
    $status:=Form.student.save() // call the server End if

    Support in 4D projects

    Ficheiros de classe (class files)

    An ORDA data model user class is defined by adding, at the same location as regular class files (i.e. in the /Sources/Classes folder of the project folder), a .4dm file with the name of the class. For example, an entity class for the Utilities dataclass will be defined through a UtilitiesEntity.4dm file.

    Criação de classes

    4D pré-criou automaticamente classes vazias na memória para cada objeto de modelo de dados disponível.

    Por padrão, as classes ORDA vazias não são exibidas no Explorer. To show them you need to select Show all data classes from the Explorer's options menu:

    As classes de utilizadores ORDA têm um ícone diferente das classes normais. As classes vazias são escurecidas:

    Para criar um arquivo de classe ORDA, basta fazer duplo clique na classe predefinida correspondente no Explorador. 4D creates the class file and add the extends code. Por exemplo, para uma classe Entity:

    Class extends Entity

    Quando uma classe for definida, o seu nome deixa de estar obscurecido no Explorador.

    Edição de classes

    To open a defined ORDA class in the 4D Code Editor, select or double-click on an ORDA class name and use Edit... from the contextual menu/options menu of the Explorer window:

    For ORDA classes based upon the local datastore (ds), you can directly access the class code from the 4D Structure window:

    Editor de método

    No Editor de Código 4D, as variáveis digitadas como uma classe ORDA se beneficiam automaticamente das características de autocompletar. Exemplo com uma variável de classe Entity: