メインコンテンツまでスキップ
バージョン: 開発中

データモデルクラス

ORDA を使用して、データモデル上に高レベルクラス関数を作成することができます。 これによってビジネス指向のコードを書き、APIのように "公開" することができます。 データストア、データクラス、エンティティ、およびエンティティセレクションはそれぞれ、関数を持つことのできるクラスオブジェクトとして提供されています。

For example, you could create a getNextWithHigherSalary() function in the EmployeeEntity class to return employees with a salary higher than the selected one. この関数は簡単に呼び出すことができます:

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

これらの関数はローカルデータストアだけでなく、クライアント/サーバーやリモートアーキテクチャーでも使用することができます:

 //$cityManager はリモートデータストアへの参照です
Form.comp.city:=$cityManager.City.getCityName(Form.comp.zipcode)

この機能により、4D アプルケーションのビジネスロジックをまるごと独立したレイヤーに保存し、高レベルのセキュリティで簡単に管理・利用することができます:

  • わかりやすく使いやすい関数のみを公開し、その裏にある構造の複雑性を "隠す" ことができます。

  • 構造が発展した場合には影響を受ける関数を適応させるだけで、クライアントアプリケーションは引き続き透過的にそれらを呼び出すことができます。

  • 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.

アーキテクチャー

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. 次の ORDA クラスが提供されています:

Class次によってインスタンス化されます
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).

ORDA データモデルユーザークラスのオブジェクトインスタンスは、それらの親クラスのプロパティや関数を使うことができます:

クラスの説明

履歴
リリース内容
19 R4Entity クラスのエイリアス属性
19 R3Entity クラスの計算属性
18 R5データモデルクラス関数は、デフォルトでは REST に公開されません。 New exposed and local keywords.

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.

例題

// cs.DataStore class

Class extends DataStoreImplementation

Function getDesc
$0:="社員と会社を公開するデータベース"

この関数は次のように使えます:

$desc:=ds.getDesc() //"社員と会社を..."

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

例題

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

全会社データから平均以上の会社データをエンティティセレクションに抽出するには次を実行します:

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

Computed attributes are defined in the Entity Class.

リモートデータストアの例

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

クライアントはまず、リモートデータストアのセッションを開始します:

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

クライアントアプリケーションは API を使い、たとえばフォームに入力された郵便番号 (zipcode) に合致する都市を取得することができます:

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

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

例題

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

任意の社員エンティティセレクションより、給与が平均以上の社員を取得するには:

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

Entity クラス

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

計算属性

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.

エイリアス属性

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.

例題

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

次のように関数を呼び出すことができます:

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

定義規則

データモデルクラスを作成・編集する際には次のルールに留意しなくてはなりません:

  • 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. 特に:

    • Do not give the same name to a 4D table and to a user class name. 衝突が起きた場合には、ユーザークラスのコンストラクターは使用不可となります (コンパイラーにより警告が返されます)。
    • 4D テーブルに予約語を使用してはいけません (例: "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.

プリエンプティブ実行

コンパイル済みの状態では、データモデルクラス関数は次のように実行されます:

  • 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).

クライアント/サーバーで動作するように設計されているプロジェクトでは、データモデルクラス関数のコードがスレッドセーフであることを確認してください。 スレッドセーフでないコードが呼び出された場合、実行時にエラーが発生します (シングルユーザーアプリケーションではコオペラティブ実行がサポートされているため、コンパイル時にはエラーが発生しません)。

計算属性

概要

計算属性は、計算をマスクするデータ型を持つデータクラス属性です。 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. 属性がアクセスされなければ、コードも実行されません。

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. デフォルトでは、ORDA のクエリで計算属性が使用された場合、その属性はエンティティ毎に一度計算されます。 場合によっては、これで十分です。 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. デフォルトでは、ORDA の並べ替えで計算属性が使用された場合、その属性はエンティティ毎に一度計算されます。 Just like in queries, computed attributes can implement an orderBy function that substitutes other attributes during the sort, thus increasing performance.

計算属性の定義

You create a computed attribute by defining a get accessor in the entity class of the dataclass. 計算属性は、データクラス属性およびエンティティ属性として自動的に利用可能になります。

Other computed attribute functions (set, query, and orderBy) can also be defined in the entity class. これらの関数の定義は任意です。

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>

シンタックス

{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.

計算属性は、他の計算属性の値を使用することができます。 再帰的な呼び出しはエラーになります。

The getter function defines the data type of the computed attribute thanks to the $result parameter. 以下の結果の型が可能です:

  • スカラー (テキスト、ブール、日付、時間、数値)
  • Object
  • ピクチャー
  • BLOB
  • エンティティ (例: cs.EmployeeEntity)
  • エンティティセレクション (例: cs.EmployeeSelection)

The $event parameter contains the following properties:

プロパティタイプ説明
attributeNameText計算属性の名称
dataClassNameTextデータクラスの名称
kindText"get"
resultVariant任意。 スカラー属性が Null を返すようにするには、このプロパティを Null値で追加します。

例題

  • 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
  • 計算属性は、エンティティにリレートされた属性に基づいて定義することができます。
Function get bigBoss($event : Object)-> $result: cs.EmployeeEntity
$result:=This.manager.manager

  • 計算属性は、エンティティセレクションにリレートされた属性に基づいて定義することができます。
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>

シンタックス


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

The setter function executes whenever a value is assigned to the attribute. この関数は通常、入力値を処理し、その結果を 1つ以上の他の属性に転送します。

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

The $event parameter contains the following properties:

プロパティタイプ説明
attributeNameText計算属性の名称
dataClassNameTextデータクラスの名称
kindText"set"
valueVariant計算属性によって処理されるべき値

例題

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>

シンタックス

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

このメソッドは 3種類のシンタックスを受け入れます:

  • 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: | プロパティ | タイプ | 説明 | | ---------------------------------- | ---------- | -------------------------------------------------------------------------------------- | | $result.query | Text | プレースホルダー (:1, :2, など) を使った有効なクエリ文字列 | | $result.parameters | Collection | プレースホルダーに渡す値 |

The query function executes whenever a query using the computed attribute is launched. インデックス付きの属性を利用することで、クエリをカスタマイズしたり最適化したりすることができます。 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:

プロパティタイプ説明
attributeNameText計算属性の名称
dataClassNameTextデータクラスの名称
kindText"query"
valueVariant計算属性によって処理されるべき値
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)
  • resultVariant計算属性によって処理されるべき値。 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.

    例題

    • 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).

    呼び出しコードの例:

    $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

    呼び出しコードの例:

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

    シンタックス

    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. これにより、計算属性で並べ替えることができます。 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:

    プロパティタイプ説明
    attributeNameText計算属性の名称
    dataClassNameTextデータクラスの名称
    kindText"orderBy"
    valueVariant計算属性によって処理されるべき値
    operatorText"desc" または "asc" (デフォルト)
    descendingBooleantrue for descending order, false for ascending order
    resultVariant計算属性によって処理されるべき値。 Pass Null if you want to let 4D execute the default sort.

    You can use either the operator or the descending property. これは、基本的にプログラミングのスタイルの問題です (例題参照)。

    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.

    例題

    次のような条件分岐のコードを書くことができます:

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

    また、次のような短縮コードを書くこともできます:

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

    場合によっては条件分岐のコードが必要です:

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

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

    エイリアス属性

    概要

    An alias attribute is built above another attribute of the data model, named target attribute. ターゲット属性には、リレートデータクラス (リレートレベルは無制限) または同じデータクラスのものを使用できます。 エイリアス属性はデータではなく、ターゲット属性へのパスを格納します。 データクラスには、必要な数だけエイリアス属性を定義することができます。

    エイリアス属性は、N対Nリレーションを扱うのに便利です。 実装の詳細ではなくビジネスの概念を扱ってコードやクエリを作成できるため、これらの可読性が向上します。

    エイリアス属性の定義

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

    Alias <attributeName> <targetPath>

    シンタックス

    {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.

    エイリアスは、他のエイリアスのパスに使用することができます。

    A computed attribute can be used in an alias path, but only as the last level of the path, otherwise, an error is returned. たとえば、"fullName" 計算属性がある場合、"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.

    エイリアス属性の使用

    エイリアス属性は読み取り専用です (同じデータクラスのスカラー属性に基づく場合は例外です; 最後の例題参照)。 エイリアス属性は、次のようなクラス関数において、ターゲット属性パスの代わりに使用することができます:

    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()

    エイリアス属性はサーバー上で計算されることに留意してください。 リモート環境において、エンティティのエイリアス属性を更新するには、エンティティをサーバーから再ロードする必要があります。

    エイリアスのプロパティ

    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).

    例題

    以下のモデルがあるとき:

    Teacher データクラスに、教師の生徒をすべて返すエイリアス属性を定義します:

    // cs.TeacherEntity class

    Class extends Entity

    Alias students courses.student //relatedEntities

    Student データクラスには、生徒の教師をすべて返すエイリアス属性を定義します:

    // cs.StudentEntity class

    Class extends Entity

    Alias teachers courses.teacher //relatedEntities

    Course データクラスには次を定義します:

    • "name" 属性を別名で参照するためのエイリアス属性
    • 教師の名前を返すエイリアス属性
    • 生徒の名前を返すエイリアス属性
    // 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

    すると、以下のクエリを実行することができます:

    // "Archaeology" の授業を検索します
    ds.Course.query("courseName = :1";"Archaeology")

    // Smith 教師が教えている授業を検索します
    ds.Course.query("teacherName = :1";"Smith")

    // 生徒 "Martin" が参加している授業を検索します
    ds.Course.query("studentName = :1";"Martin")

    // M. Smith 教師の生徒を検索します
    ds.Student.query("teachers.name = :1";"Smith")

    // M. Martin を生徒に持つ教師を検索します
    ds.Teacher.query("students.name = :1";"Martin")
    // シンプルなクエリ文字列で複雑なクエリを実行していることに注目してください
    // 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:

    // エイリアス属性を使って、授業の名称を変更します
    $arch:=ds.Course.query("courseName = :1";"Archaeology")
    $arch.courseName:="Archaeology II"
    $arch.save() //courseName と name は "Archaeology II" に変更されます

    公開vs非公開関数

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

    リモートリクエストには次のものが含まれます:

    • Requests sent by remote 4D applications connected through Open datastore
    • RESTリクエスト

    通常の 4Dクライアント/サーバーリクエストは影響されません。 このアーキテクチャーにおいては、データモデルクラス関数は常に利用可能です。

    公開されていない関数はリモートアプリケーションで利用することができず、RESTリクエストによるオブジェクトインスタンスに対して呼び出すこともできません。 リモートアプリケーションが非公開関数をアクセスしようとすると、"-10729 (未知のメンバー機能です)" エラーが返されます。

    To allow a data model class function to be called by a remote request, you must explicitly declare it using the exposed keyword. シンタックスは次の通りです:

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

    例題

    公開された関数によって、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:=...

    呼び出し元のコードは次の通りです:

    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"

    ローカル関数

    By default in client/server architecture, ORDA data model functions are executed on the server. 関数リクエストとその結果だけが通信されるため、通常はベストパフォーマンスが提供されます。

    しかしながら、状況によってはその関数はクライアント側で完結するものかもしれません (たとえば、すでにローカルキャッシュにあるデータを処理する場合など)。 In this case, you can save requests to the server and thus, enhance the application performance by inserting the local keyword. シンタックスは次の通りです:

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

    このキーワードを使うと、関数は常にクライアントサイドで実行されます。

    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.

    最終的にサーバーへのアクセスが必要になっても (ORDAキャッシュが有効期限切れになった場合など) 関数は動作します。 もっとも、それではローカル実行によるパフォーマンスの向上は見込めないため、ローカル関数がサーバー上のデータにアクセスしないことを確認しておくことが推奨されます。 サーバーに対して複数のリクエストをおこなうローカル関数は、サーバー上で実行されて結果だけを返す関数よりも非効率的です。 たとえば、Schools Entityクラスの次の関数を考えます:

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

    例題

    年齢の計算

    Given an entity with a birthDate attribute, we want to define an age() function that would be called in a list box. この関数をクライアントサイドで実行することで、リストボックスの各行がサーバーへのリクエストを生成するのを防ぎます。

    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

    属性のチェック

    クライアントにロードされ、ユーザーによって更新されたエンティティの属性について、サーバーへ保存リクエストを出すまえに、それらの一貫性を検査します。

    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

    呼び出し元のコード:

    var $status : Object

    // Form.student は全属性とともにロードされており、フォーム上で更新されました
    $status:=Form.student.checkData()
    If ($status.success)
    $status:=Form.student.save() // サーバーを呼び出します
    End if

    4D IDE (統合開発環境) におけるサポート

    クラスファイル

    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.

    クラスの作成

    各データモデルオブジェクトに関わるクラスは、4D によってあらかじめ自動的にメモリ内に作成されます。

    空の ORDA クラスは、デフォルトではエクスプローラーに表示されません。 To show them you need to select Show all data classes from the Explorer's options menu:

    ORDA ユーザークラスは通常のクラスとは異なるアイコンで表されます。 空のクラスは薄く表示されます:

    ORDA クラスファイルを作成するには、エクスプローラーで任意のクラスをダブルクリックします。 4D creates the class file and add the extends code. たとえば、Entity クラスを継承するクラスの場合は:

    Class extends Entity

    定義されたクラスはエクスプローラー内で濃く表示されます。

    クラスの編集

    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:

    コードエディター

    4Dコードエディターにおいて、ORDA クラス型として定義された変数は、自動補完機能の対象となります。 Entity クラス変数の例です: