v13以前のバージョンでPHP Execute
を実行した場合,アプリケーションのResourcesフォルダーに作成されるphp.ini
ファイルには,下記の1行が存在します。当時のPHPは,GDライブラリが外部DLLとしてコンパイルされており,起動時にロードする必要がありました。
extension=php_gd2.dll
17r5では,PHPがバージョン4
(32ビット版)から7
(64ビット版)にアップデートされました。このバージョンでは,GDライブラリが実行ファイルに埋め込まれているため,上記の1行は不要となりました。
v13で作成されたアプリケーションを17r5に変換した場合,PHP Execute
でエラー500
が返されるかもしれません。Windows 10 (1903) 以降,無効なDLLロードで例外が返されるようになったことが関係しているようです。
php.ini
ファイルの前述した行を削除またはコメントアウトすることにより,問題を回避することができます。
ボタン型のフォームオブジェクトにForm.button
のような数式を設定することはできません。これは仕様です。チェックボックス等とは違い,ボタンに標準アクションとオブジェクトメソッドの両方が設定できることが関係しています。内部的なコードが見直された影響で,Form.button
のような数式はボタンに設定できないことになりました。
On System Event
は,アプリケーションが最前面から移動したときなどに発生するデータベースイベントです。On System Event
データベースメソッドが存在する場合,システムイベントを監視するためにウィンドウを表示しない常駐プロセスが作成されます。このプロセスでMESSAGE
コマンドを実行した場合,「Apple Event Manager」ウィンドウが表示され,閉じることができません。これは仕様です。なお,「Apple」という名称は歴史的な経緯によるものであり,オペレーションシステムとは無関係です。
MESSAGE
を使用する場合,明示的にウィンドウを作成することが推奨されています。ウィンドウを作成せずにMESSAGE
を使用した場合,カレントプロセスの非表示ウィンドウが暗黙的に使用され,プロセスが消滅すると同時にそのウィンドウが閉じられます。それが常駐プロセスだった場合,プロセスとMESSAGE
コマンドは互いに相手が終了するまで待機することになり,ウィンドウ/プロセスを閉じることができません。
MESSAGE
コマンドを使用していないのにも関わらず「Apple Event Manager」ウィンドウが表示される場合,空のOn System Event
データベースメソッドが存在する可能性があります。システムイベントを監視する必要がある場合,イベント処理ループがブロックしないよう,新規プロセスを作成することができます。
4D v17 R5では、オブジェクトを操作してファイルあるいはフォルダの属性を検索するのがいかに簡単かをご紹介しました。この機能はオブジェクト記法のおかげでさらに簡単になっています!このブログ投稿では、ファイルとフォルダを関するインターフェイスを使った例題を公開しています。これは、4Dでは新しいことではなく、これまでの4Dコマンドでも可能でした。しかし、このバージョンからより簡単にわずかんコードでできるようになりました。
このTipでは、オブジェクトを操作してファイルやフォルダの属性を検索するのがいかに簡単かをご覧いただきます。さらに良いことに、新しい機能を組み合わせることでより強力になることもお見せしています。File とfolder コマンド、オブジェクト記法や新しいリストボックス 機能などです。例えば、リストボックス に表示するファイルやフォルダのコレクションを検索するには:
Form.docs:=Form.curfolder.folders().concat(Form.curfolder.files())
あなた自身がサンプルを試してみてください。ディスクファイル・ブラウザーのように動くことが分かるでしょう。あなたならこの機能のためにリストボックス の裏でどのようにコードを書くのか、ちょっと考えてみてください。最後に、デザインモードに移ってコードを見てみると、いかに短いコードが使われているかに驚くことでしょう。オブジェクト記法を使わずに同じことをしてみると、オブジェクトには10倍以上のコーディングが必要です。まだ、オブジェクト記法を試していないのなら、今こそその時です!
これまでにクライアントと4D Server間で送信されるORDAのリクエストのトラフィックを分析する必要を感じたことはありませんか?時にサーバーからレスポンスを受け取るまでに間があることがあります。それがネットワークのトラフィックのせいなのか、あるいは最適化されていないリクエストを書いたせいなのか考えさせられる場合があります。幸いなことに、4D V17 R6は、ds オブジェクトで使用可能な新しいORDAメソッドを使って、この遅延の原因を特定することができます。これらはデバッギング機能であるだけではなく、送信されたリクエストをよりよく理解してORDAコードを最適化することもできるようになりました。
startRequestLog()メンバー・メソッドはとてもフレキシブルです。ORDAリクエストログをファイルもしくはメモリに取ることができます。
ファイルにログを取るには、シンプルにstartRequestLog()を、ORDAリクエストがログインする場所を示すFileオブジェクトとともにコールするだけです。以下の例では、時々クエリーをインデックス化されていないフィールド上で走らせます。この新しい機能のおかげで、リクエストの継続時間をチェックできます:
C_OBJECT($first;$e)
C_COLLECTION($log)
ds.startRequestLog(10) // Only the last 10 requests will be kept in memory
$first:=ds.Persons.all().first()
$e:=ds.Persons.query("name=:1";"Brown")
$log:=ds.getRequestLog()
ALERT("The longest request lasted: "+String($log.max("duration"))+" ms")
ds.stopRequestLog()
各リクエストはオブジェクトのJSON表現としてログを取られます。
以下はORDARequests.txtファイルの内容です:
[
{
...
"startTime":"2019-07-02T12:33:25.922Z",
"endTime":"2019-07-02T12:33:27.681Z",
"duration":4200,
"response":{ ...}
...
}
]
各リクエストはコレクションの中にオブジェクトとしてログを取られます。getRequestLog() メンバー・メソッドを使って検索することができます。
コレクションを返すので、可能なコレクション・メソッドは全て使用できます。
C_OBJECT($first;$e)
C_COLLECTION($log)
ds.startRequestLog(10) // Only the last 10 requests will be kept in memory
$first:=ds.Persons.all().first()
$e:=ds.Persons.query("name=:1";"Brown")
$log:=ds.getRequestLog()
ALERT("The longest request lasted: "+String($log.max("duration"))+" ms")
ds.stopRequestLog()
上記のサンプルが示す通り、ORDAリクエストのログを止めるには、stopRequestLog() メンバー・メソッドをコールするだけです。
ダイナミックフォームの機能は、4D v16 R6で導入され、オブジェクト内でフォームを構築したり、テキストファイルからフォームをロードしたりすることで、その場でフォームを構築できます。これは、アプリケーションのニーズに合わせてフォームが頻繁に変更される世界ではとても便利です。
4Dでは、バイナリーフォームでもダイナミックフォームでも、入力順は典型的なz順に従います。4D v17 R6を使うと、z順に関連づけることなく入力順を定義できるようになりました。
フォーム定義の各ページには、z順に従って配置されたフォーム要素のリストを含む”object”属性があります。新しい”entryOrder”属性が追加され、独自のカスタマイズした入力順を指定できるようになりました。もしこの属性が未定義の場合は、4Dはz順(”objects”属性で定義された順序)を使います。
以下は、二つの入力フィールドとボタンを使ったダイナミックフォームの例です:
// Create inputs and button
$text1:=New object("type";"input"; "top";20; "left";140; "width";100; "height";18)
$text2:=New object("type";"input"; "top";20; "left";20; "width";100; "height";18)
$button:=New object("type";"button"; "text";"OK"; "top";60; "left";140; "width";100; "height";20)
// Create entry order collection
$entryOrder:=New Collection("text2";"text1")
// Create page with form objects and entry order
$page:=New object("objects";New object("text1";$text1; "text2";$text2; "button";$button); "entryOrder";$entryOrder)
// Create form
$form:=New object("pages"; New collection(Null;$page); "windowTitle";"My form"; "rightMargin";20; "bottomMargin";20)
// Load the form
$w:=Open form window($form)
DIALOG($form)
これは次のように表すことができます:
{ "pages": [ null, { "objects": { "text1": { "type": "input", "top": 20, "left": 140, "width": 100, "height": 18, "events": ["onClick"] }, "text2": { "type": "input", "top": 20, "left": 20, "width": 20, "height": 18, "events": ["onClick"] }, "button": { "type": "button", "text": "OK", "top": 60, "left": 140, "width": 20, "height": 20, "events": ["onClick"] } }, "entryOrder": [ "text2", "text1" ] } ] }
4D View ProはRリリース毎にさらに機能豊かになりつつあり、4D v17 R6も例外ではありません!新しいコマンドによって、4Dメソッドのパラメータ、名前、タイプとサマリーを特定することができます。そして、あなたのメソッドがより情報豊かで説明的になり、それらをエンドユーザーが使う助けになります。
日にちや時間を宣言したい場合は?そのメソッドが何をするのかをユーザーが理解するのを補助するために、短い説明を与えるのはいかがでしょう。VP SET ALLOWED METHODS コマンドで実行できるようになりました!
サンプルよりもこのコマンドを使う方法をより良く知る方法は何でしょう?あなたがサンプルの手順を読み終えて実行する頃までに、以下の結果が得られていることでしょう:
VP SET ALLOWED METHODSコマンドは、4Dメソッドを実行する新しい方法を提供します。以下のデモは4D View Proの式中にある”Birth Information” メソッドを呼び出したい場合にどのようにするかを示しています:
C_TEXT($1)
C_DATE($2)
C_TIME($3)
C_TEXT($0)
// Create a string from parameters
$0:=$1+" was born on "+String($2)+" at "+String($3)
オブジェクトを作成して、メソッドのコールネームを定義するためにオブジェクトの属性を使います:
$o:=New object
// Name of the method in 4D View Pro: "Birth Information"
$o.BIRTH_INFORMATION:=New object
次に$o.BIRTH_INFORMATION オブジェクトの中で、呼び出す4Dメソッドを定義します:
$o.BIRTH_INFORMATION.method:="Birth Information"
メソッドのパラメータについて情報を持ったコレクションを追加します:
$o.BIRTH_INFORMATION.parameters:=New collection
$o.BIRTH_INFORMATION.parameters.push(New object("name";"First name";"type";Is text))
$o.BIRTH_INFORMATION.parameters.push(New object("name";"Birthday";"type";Is date))
$o.BIRTHparameters.push(New object("name";"Time of birth";"type";Is time))
コマンドが何をするのかについてサマリーを特定します:
$o.BIRTH_INFORMATION.summary:="Returns a string from birth information"
最後に、4D View Pro式の中にある引数の数を、最小値と最大値を追加することで制限できます。例えば、パラメータの全てが必須の場合:
$o.BIRTH_INFORMATION.minParams:=3
$o.BIRTH_INFORMATION.maxParams:=3
メソッドが必要とする分だけ属性を作成することができます。それからメソッドオブジェクト($o)をVP SET ALLOWED METHODS コマンドに渡します:
VP SET ALLOWED METHODS ($o)
CHANGE CURRENT USER
のドキュメントには,下記のような記載がありました。
このコマンドを使用するためにはアクセス管理システムが有効化されている必要があります。言い換えると,Designer にパスワードが割り当てられている必要性があります。そうでない場合, CHANGE CURRENT USER には何の効力もなく,ユーザーを切り替えるための標準ウィンドウは表示されません。
v14以降,Designerのパスワードが設定されていない場合,CHANGE CURRENT USER
のパスワードチェックは省略されるようになりました。つまり,コマンドでカレントユーザーを切り替えるために,Designerのパスワードを設定する必要はありません。この点は,17r5以降のドキュメントに反映されています。
16r4以降,Undefined
は「参照」ではなく「値」を受け取るように改定されました。下記のようなコードをコンパイルモードで実行した場合,「ポインターの使用法が正しくないか,不明な変数へのポインターです」というエラーが返されます。
C_POINTER($W)
$W:=Get pointer("toto")
If (Undefined($W->))
ALERT("undefined")
End if
未定義のプロセス変数に対するポインターを検出したいのであれば,Undefined($W->))
ではなく,Type($W->)=Is undefined
のようなコードを記述することができます。
前のブログ投稿で、ORDA クエリーの中で式を適用して、洗練された複雑な検索条件をどのように構築するかを説明しました。さらに完全で強力な開発ツールを使って提供できるのが、新しいORDA メソッドのorderByFomula() です。formulas の助けによって、プロジェクト・メソッドあるいは4D式の中で複雑な条件を使ってエンティティ・セレクションのソートができます。
ソート条件は、式がストリングあるいは Formula オブジェクトとしてorderByFormula() メソッドに与えられた時に評価されます。
ここにデータクラス、Companiesがあります。これからこの企業を収益(収益 = 収入 - 支出)を基にソートします。
エンティティはThis コマンドでアクセスすることに注意しましょう。
C_OBJECT($companies;$orderedCompanies;$formula)
$formula:= Formula(This.revenue - This.costs)
$companies:=ds.Companies.all()
$orderedCompanies:=$companies.orderByFormula ($formula;dk descending)
では同じ条件を使って企業をソートしますが、ストリングの代わりに、Formula オブジェクトを使い、降順でソートします。
C_OBJECT($companies;$orderedCompanies;$formula)
$formula:= Formula(This.revenue - This.costs)
$companies:=ds.Companies.all()
$orderedCompanies:=$companies.orderByFormula ($formula;dk descending)
Formula オブジェクトを使うことで、式をストリングとして使うよりもいくつかの利点(自動補完、コードエディター内の色指定、など)があります。
orderByFomula() メソッドはさらに多くのオプションがあります。詳しくはドキュメントをチェックし、上記のサンプルデータベース (HDI)をダウンロードしてください。
query() メンバーメソッド自体の内部にorder by ステートメントを挿入することができます。これはクラアント/サーバー・モードで使う場合には大きな利点で、クエリーとソートアクションを同じリクエスト中に組み合わせることができ、サーバーとネットワークトラフィックに送られるリクエストの数を減らします。
C_OBJECT($companies)
$companies:=ds.Companies.query("revenue> :1 order by costs desc";5000)