文字列式が、文字列ではなく通常の式として処理されます。
ダイナミック変数について
ダイナミック変数とは、ダイナミックに名前が付けられる変数のことで、多くの場合、変化しない部分と変化する部分から構成されます。例えば次の例では、変化する接頭辞と変化しない接尾辞によって変数名がダイナミックに生成されます。
<cfset "#flavor#_availability" = "out of stock">
このようにダイナミック変数を使用する場合は、ダイナミック評価は不要です。
ダイナミック式とダイナミック評価について
ダイナミック評価は次の手順で実行されます。
-
-
式が解析されて、式の要素が決定されるとともに、式のシンタックスが検証されます。
-
式が評価されます。その際には、変数が値に置き換えられ、関数が呼び出され、その他の必要な操作が行われます。
この処理によって、変化する部分を持つダイナミック式が解釈されます。ただし、この処理ではかなりのオーバーヘッドが生じます。
ダイナミック式は、初期バージョンの ColdFusion (配列や構造体がサポートされる前)では重要な機能でした。現在でも特定の状況では役に立ちますが、構造体の機能や、連想配列の表記法を使用して構造体の要素にアクセスする機能を使用すれば、効率よく簡単にダイナミックなデータ管理が行えます。配列および構造体の使用については、配列および構造体の使用を参照してください。
変数名の作成方法の選択
ダイナミック変数名が必要になる例を 2 つ示します。
- フォームのフィールドの数や名前がダイナミックに変化するフォームアプリケーション。この場合、フォームからアクションページに渡されるのはそのフィールドの名前と値のみです。アクションページでは、実際に作成されたフィールド名(変数名)は把握できますが、すべてのフィールド名を把握することは不可能です。
- 次の条件が当てはまる場合
- ColdFusion がカスタムタグを複数回呼び出す。
- カスタムタグの結果を、毎回異なる変数に返す必要がある。
- カスタムタグの結果を返す変数を、呼び出し側のコードが指定できる。
この場合、カスタムタグは結果を返す変数名をあらかじめ把握できないので、それを属性値として取得します。
いずれの場合も、Evaluate 関数を使用したダイナミック式で変数名を構築する必要があるように思えるかもしれませんが、「例:ダイナミックショッピングカート」に示すように、ダイナミック変数名を使用すれば、より効率的に同じ結果を得ることができます。
これは、ダイナミック評価は常に避けるべきであるという意味ではありません。しかし、ダイナミック評価はパフォーマンスコストを必要とするので、まず次の方法で同じ結果が得られないかを確認してください。
- 配列(インデックス変数を使用)
- 式を使用して連想配列の表記法で構造体の要素にアクセスする
- 変数名をダイナミックに生成する
ダイナミック評価を使用しないダイナミック変数名
ColdFusion では、変化する部分を組み合わせてインラインで変数名を生成する方法を常に使用できるわけではありませんが、一般的な用途にはこの方法で対応できます。
代入式で # 記号を使用して変数名を構築する
cfset の代入式の左辺で、テキストと変数名を組み合わせて変数名を構築することができます。例えば次のコードでは、変数 Product12 に文字列値 "Widget" が設定されます。
<cfset "Product#ProdNo#" = "Widget">
この方法で変数名を構築する場合は、等号の左辺のテキストをすべて引用符で囲む必要があります。
この方法は、配列を使用する方法よりも効率が落ちます。次の例では、より少ない処理で前の例と同じ目的を達成できます。
<cfset prodNo = 12> <cfset myArray[prodNo] = "Widget">
ダイナミック変数の制約
代入式の左辺に引用符で囲んだダイナミック変数名を使用する場合、その名前は、単純変数名か、object.property の表記法を使用した複合名(MyStruct.#KeyName# など)のいずれかである必要があります。ダイナミック変数名に配列を含めることはできません。例えば、次のコードはエラーになります。
<cfset productClassNo = 1> <cfset productItemNo = 9> <cfset "myArray[#productClassNo##productItemNo#]" = "Widget">
ただし、代入式の左辺で引用符を使用せずに、配列のインデックス値を変数からダイナミックに作成することは可能です。例えば、前述のサンプルコードは、最後の行を次の行に置き換えれば正しく動作します。
<cfset myArray[#productClassNo# & #productItemNo#] = "Widget">
構造体の参照をダイナミックに構築する
連想配列の表記法を使用して構造体を参照することができるので、変数を使用して構造体の参照をダイナミックに作成することができます。連想配列の表記法については、構造体の表記法を参照してください。連想配列の表記法で構造体を参照するときには、インデックスの括弧内に ColdFusion 式を使用することができます。例えば、product_1、product_2、... というキーを持つ productName 構造体がある場合、次のコードを実行すると productName.product_3 の値が表示されます。
<cfoutput> Product_3 Name: #ProductName["product_" & prodNo]# <cfoutput>
この形式を使用したショッピングカート管理の例については、「例:ダイナミックショッピングカート」を参照してください。
ダイナミック評価の使用
ダイナミック評価とダイナミック式には、いくつかの特徴や注意事項があります。
ColdFusion ダイナミック評価関数
次の表に、ダイナミック評価を実行する関数と、ダイナミック式の評価に役立つ関数を示します。
関数 |
用途 |
---|---|
引数内の二重引用符(")をエスケープし、結果を二重引用符で囲みます。DE 関数は、出力文字列が IIF 関数で評価されないようにしたい場合に便利です。IIF 関数で DE 関数を使用する例については、「IIF 関数の使用」を参照してください。 |
|
1 つまたは複数の文字列式を引数に取り、それらの内容を式と見なして、左から右にダイナミックに評価していきます。左側の式の評価結果 が、 右側の式に影響を与えることがあります。右端の引数の評価結果を返します。この関数の詳細については、「Evaluate 関数について」を参照してください。 |
|
ブール条件式を評価します。この式が True であるか False であるかによって、2 つの文字列式のいずれかをダイナミックに評価し、その結果を返します。IIF 関数は、 cfif タグ を HTML にインラインで組み込みたい場合に便利です。この関数の使用例については、「IIF 関数の使用」を参照してください。 |
|
任意精度の 10 進演算が可能である点を除いて、Evaluate 関数と同じです。数式内のオペランドが 10 進数(12947834.986532 など)であり、ColdFusion の数値データ型で正確に表現できないほど長い場合、この関数は任意精度の演算を行って結果を計算 し、 その結果を任意長の数値の文字列として返します。この関数について詳しくは、『CFML リファレンス』の PrecisionEvaluate を参照してください。 |
|
最初の引数で表現されている変数に、2 番目の引数で指定されている値を設定します。現在、適切な形式で記述された ColdFusion ページでは、この関数を使用する必要はなくなりました。「SetVariable 関数に関する注意事項」を参照してください。 |
関数の引数の評価に関する注意事項
ColdFusion では、引数の値が関数に渡される前に、引数の評価が必ず実行されることに注意する必要があります。
例えば、次の DE 関数の例を考えてみます。
<cfoutput>#DE("1" & "2")#</cfoutput>
この結果は """1"" & ""2""" となるように思うかもしれませんが、ColdFusion では次のような処理が行われるので、実際には「12」と表示されます。
-
式 "1" & "2" が文字列 "12" と評価されます。
-
文字列 "12"(引用符は除く)が DE 関数に渡されます。
-
DE 関数が呼び出され、12 の前後に引用符が付加されます。
同様に、DE(1 + 2) という式の場合は、1 + 2 が整数 3 と評価されて、関数に渡されます。関数によって整数 3 が文字列に変換され、リテラル引用符で囲まれて "3" という結果が返されます。
Evaluate 関数について
Evaluate 関数は、1 つまたは複数の文字列式を引数に取り、それらの内容を式と見なして左から右にダイナミックに評価していき、右端の引数の評価結果を返します。
ここでは、次のコードを例に取って、Evaluate 関数と ColdFusion 変数の処理方法を説明します。
<cfset myVar="27/9"> <cfoutput> #myVar2#<br> #myVar#<br> #Evaluate("myVar2")#<br> #Evaluate("myVar")#<br> #Evaluate(myVar2)#<br> #Evaluate(myVar)#<br> </cfoutput>
コードの説明
このコードが ColdFusion で処理される手順を、次の表に示します。
コード |
説明 |
---|---|
myVar2="myVar"><cfset myVar="27/9"> |
2 つの変数に文字列 myVar および 27/9 が設定されます。 |
<cfoutput>#myVar2# <br/>#myVar#<br/> |
各変数に代入されている myVar および 27/9 という値が表示されます。 |
#Evaluate("myVar2")# <br> |
文字列 "myvar2"(引用符は除く)が Evaluate 関数に渡され、次の処理が行われます。1. 変数 myVar2 と評価されます。2. myVar2 変数の値である文字列 "myvar"(引用符は除く)が返されます。 |
#Evaluate("myVar")# <br> |
文字列 "myVar"(引用符は除く)が Evaluate 関数に渡され、次の処理が行われます。1. 変数 myVar と評価されます。2. myVar 変数の値である文字列 "27/9"(引用符は除く)が返されます。 |
#Evaluate(myVar2)# <br> |
変数 myVar2 が文字列 "myVar" と評価され、この文字列(引用符は除く)が Evaluate 関数に渡されます。残りの処理は、前のコードと同じです。 |
#Evaluate(myVar)# <br/></cfoutput> |
変数 myVar が文字列 "27/9"(引用符は除く)と評価されます。この文字列が Evaluate 関数に渡され、次の処理が行われます。1. この文字列が式 27/9 と評価されます。2. 除算が実行されます。3. その結果である値 3 が返されます。 |
このように、ダイナミック式を使用すると、式を評価するためにかなりのオーバーヘッドが生じ、コードもわかりにくくなります。したがって、インデックス配列や構造体などの単純な方法で同じ処理が行える場合は、ダイナミック式を使用しないことをお勧めします。
Evaluate 関数の使用の回避
Evaluate 関数を使用すると、処理オーバーヘッドが増大します。ほとんどの場合、この関数を使用する必要はありません。次の例は、Evaluate 関数を使用するかどうかを検討したほうがよいケースを示しています。
例 1
次のようなコードでは、Evaluate 関数を使用したくなるかもしれません。
<cfoutput>1 + 1 is #Evaluate(1 + 1)#</cfoutput>
このコードは動作しますが、次のコードの方が効率的です。
<cfoutput>1 + 1 is #Result#</cfoutput>
例 2
この例は、Evaluate 関数の代わりに連想配列の表記法を使用する方法を示しています。この方法は強力です。その理由は次のとおりです。
- ほとんどの ColdFusion スコープは構造体としてアクセスできます。
連想配列の表記法で構造体を参照するときには、インデックスに ColdFusion 式を使用することができます。連想配列の表記法で構造体を参照する方法の詳細については、「構造体の表記法」を参照してください。
次の例では、Evaluate 関数を使用して変数名を生成しています。
このコードは、ショッピングカートに入っている不定数の品目に対してエントリが作成されるフォームで使用されているものです。ショッピングカートの各品目に対して、製品名フィールドが用意されます。このフィールドの名前は、product_1、product_2 という形式になっています。数字の部分は、ショッピングカート内の製品エントリを表します。この例では、ColdFusion によって次の処理が行われます。
-
変数 i がその値(1 など)に置き換えられます。
-
その変数値と "Form.product_" が連結され、その結果(Form.product_1)が Evaluate 関数に渡されます。以降の手順は、この関数によって実行されます。
-
変数 product_1 が解析され、変数の実行可能表現が生成されます。ColdFusion はパーサーを実行する必要があるので、単純変数であってもこの手順には時間がかかります。
-
変数表現が評価されます。例えば、"空気銃" などと評価されます。
-
変数の値が返されます。
次の例を使用すれば、前の例と同じ結果を、より効率的に得ることができます。Product Name: #Evaluate("Form.product_#i#")# </cfoutput>
このコードでは、ColdFusion によって次の処理が行われます。
-
連想配列のインデックスの括弧内にある式が評価され、文字列 "product_" に変数 i の値を連結するものと解釈されます。
-
変数 i の値が 1 と決定されます。
-
文字列と変数値が連結されて product_1 が取得されます。
-
その結果が Form 構造体のキー値として使用され、Formproduct_1 が取得されます。この連想配列の表記法を使用した参照でも、object.attribute という形式の参照である Form.product_1 と同じ値(この例では "空気銃")が取得されます。
このコードではダイナミック評価を使用していませんが、文字列と変数を使用することで、構造体への参照を同じくダイナミックに作成しています。
SetVariable 関数に関する注意事項
次のような方法を使用して変数名をダイナミックに生成すれば、SetVariable 関数の使用を避けることができます。例えば、次の行は同等です。
<cfset "myVar#i#" = myVal>
2 番目のコードでは、変数名 myVar#i# が引用符で囲まれているので、ColdFusion によってその名前の評価が行われ、# 記号で囲まれているテキストが変数または関数として処理されます。#i# は変数 i の値に置き換えられます。i の値が 12 の場合、このコードは次のコードと等しくなります。
<cfset myVar12 = myVal>
この方法の詳細については、このページの代入式で # 記号を使用して変数名を構築するを参照してください。
IIF 関数の使用
IIf 関数は、次のコードを簡略化したものです。
<cfset result = Evaluate(argument1)> <cfelse> <cfset result = Evaluate(argument2)> </cfif>
IIF 関数では、評価を行わないリテラル文字列は DE 関数の中に入れる必要があります。次に例を示します。
#IIf(IsDefined("LocalVar"), "LocalVar", DE("The variable is not defined."))# </cfoutput>
文字列 "The variable is not defined." を DE 関数で囲まないと、IIF 関数が文字列の内容を式として評価しようとするので、エラーになります。この場合は、無効なパーサー構文エラーになります。
IIF 関数は、HTML コードにインラインで ColdFusion ロジックを組み込む場合に便利ですが、ダイナミック式の評価を必要としない場合には、処理に時間がかかるという欠点があります。
次の例では、IIF を使用して表の行の背景色を 1 行おきに白と灰色に設定します。また、DE 関数を使用して、ColdFusion が色文字列を評価しないようにしています。
<table border="1" cellpadding="3"> <cfloop index="i" from="1" to="10"> <tr bgcolor="#IIF( i mod 2 eq 0, DE("white"), DE("gray") )#"> <td> hello #i# </td> </tr> </cfloop> </table> </cfoutput>
このコードは、IIF や DE を使用しない次の例よりも簡潔です。
<table border="1" cellpadding="3"> <cfloop index="i" from="1" to="10"> <cfif i mod 2 EQ 0> <cfset Color = "white"> <cfelse> <cfset Color = "gray"> </cfif> <tr bgcolor="#color#"> <td> hello #i# </td> </tr> </cfloop> </table> </cfoutput>
例:ダイナミックショッピングカート
次の例では、連想配列の表記法を使用することで、ダイナミック式の評価を行わずに変数名をダイナミックに作成および操作しています。
ショッピングカートのように、必要な出力がダイナミックに生成され変化するようなアプリケーションでは、変数名をダイナミックに生成する必要があります。ショッピングカートでは、カートのエントリ数やその内容を予測することは不可能です。また、フォームを使用しているので、アクションページが受け取るのはフォームフィールドの名前と値を持つ Form 変数のみに限られます。
次の例では、ショッピングカートの内容が表示され、ユーザーは注文内容を編集して送信できます。例を単純にするために、ユーザーがカートに項目を入れるのではなく、CFScript を使用して自動的にショッピングカートの内容を生成しています。より完全な例では、ユーザーの選択した項目をショッピングカートに入れることになります。またこの例では、注文の確認や発注などのビジネスロジックも省略されています。
フォームの作成
- エディタでファイルを作成します。
<head> <title>Shopping Cart</title> </head> <cfscript> CartItems=4; Cart = ArrayNew(1); for ( i=1; i LE cartItems; i=i+1) { Cart[i]=StructNew(); Cart[i].ID=i; Cart[i].Name="Product " & i; Cart[i].SKU=i*100+(2*i*10)+(3*i); Cart[i].Qty=3*i-2; } </cfscript> <body> Your shopping cart has the following items.<br> You can change your order quantities.<br> If you don't want any item, clear the item's check box.<br> When you are ready to order, click submit.<br> <br> <cfform name="ShoppingCart" action="ShoppingCartAction.cfm" method="post"> <table> <tr> <td>Order?</td> <td>Product</td> <td>Code</td> <td>Quantity</td> </tr> <cfloop index="i" from="1" to="#cartItems#"> <tr> <cfset productName= "product_" & Cart[i].ID> <cfset skuName= "sku_" & Cart[i].ID> <cfset qtyname= "qty_" & Cart[i].ID> <td><cfinput type="checkbox" name="itemID" value="#Cart[i].ID#" checked> </td> <td><cfinput type="text" name="#productName#" value="#Cart[i].Name#" passThrough = "readonly = 'True'"></td> <td><cfinput type="text" name="#skuName#" value="#Cart[i].SKU#" passThrough = "readonly = 'True'"></td> <td><cfinput type="text" name="#qtyName#" value="#Cart[i].Qty#"> </td> </tr> </cfloop> </table> <input type="submit" name="submit" value="submit"> </cfform> </body> </html>
- このファイルに "ShoppingCartForm.cfm" という名前を付けて保存します。
コードの説明
このコードについて、次の表で説明します。
コード |
説明 |
---|---|
Cart = ArrayNew(1); |
構造体の配列としてショッピングカートを作成します。各構造体には、カート品目 ID、製品名、SKU 番号、およびそのカートでの品目の注文個数が含まれます。CartItems の数だけループし、ループカウンタに基づいて構造体の変数に値を設定して、ショッピングカートに製品を入れます。実際のアプリケーションでは、一般に、名前、SKU、および数量の値は別のページで設定します。 |
<tr> |
フォームとそれに埋め込まれたテーブルを開始します。ユーザーが送信ボタンをクリックすると、フォームのデータが ShoppingCartAction.cfm ページに送信されます。テーブルによってフォームの形式を整えています。テーブルの最初の行は、列のヘッダです。その後の各行に、カート内の品目のデータが表示されます。 |
<cfset productName= "product_" & Cart[i].ID> |
ショッピングカートのエントリをループして、カートフォームをダイナミックに生成しています。ループごとに、フィールドタイプ識別子(「sku_」など)の後にカート品目 ID(Carti.ID)を付加して、フォームフィールドの name 属性に使用する変数を生成しています。すべてのチェックボックスに対して、"itemID" という同じ名前を使用しています。したがって、アクションページに送信される itemID の値は、すべてのチェックボックスフィールドの値が列挙されたリストになります。各品目のチェックボックスフィールドの値は、カート品目 ID です。行の各列は、カート品目構造体のエントリになっています。passthrough 属性によって、製品名と SKU フィールドが読み取り専用に設定されています。単一引用符(')を使用していることに注意してください。cfinput タグの passthrough 属性について詳しくは、『CFML リファレンス』を参照してください。チェックボックスはデフォルトで選択されています。 |
</cfform> |
送信ボタンを作成してフォームを終了します。 |
アクションページの作成
- エディタでファイルを作成します。
次のテキストを入力します。
<head> <title>Your Order</title> </head> <body> <cfif isDefined("Form.submit")> <cfparam name="Form.itemID" default=""> <cfoutput> You have ordered the following items:<br> <br> <cfloop index="i" list="#Form.itemID#"> ProductName: #Form["product_" & i]#<br> Product Code: #Form["sku_" & i]#<br> Quantity: #Form["qty_" & i]#<br> <br> </cfloop> </cfoutput> </cfif> </body> </html>
- このファイルに「ShoppingCartAction.cfm」という名前を付けて保存します。
- ブラウザーで ShoppingCartform.cfm を開き、チェックボックスと数量の値を変更し、送信ボタンをクリックします。
コードの説明
このコードについて、次の表で説明します。
コード |
説明 |
---|---|
<cfif isDefined("Form.submit")> |
フォーム送信によって呼び出された場合にのみ、このページの CFML を実行します。これは、フォームページとアクションページを分離している場合には必要ありませんが、1 つの ColdFusion ページにフォームとアクションを記述している場合には必要です。 |
<cfparam name="Form.itemID" default=""> |
Form.itemID のデフォルトを空の文字列に設定します。これは、ユーザーがすべてのチェックボックスをオフにしてフォームを送信しても(製品 ID が 1 つも送信されない場合でも)、ColdFusion でエラーが表示されないようにするためです。 |
You have ordered the following items:<br> |
注文された各品目について、名前、SKU 番号、および数量を表示します。フォームページから送信される Form.itemID は、すべてのチェックボックスの value 属性が列挙されたリストです。この属性は、選択されたショッピングカート品目の ID を表します。リストの値をインデックスに使用してループし、注文した各品目を出力しています。連想配列の表記法を使用して Form スコープに構造体としてアクセスし、配列のインデックスで式を使用してフォーム変数名を生成しています。この式では、フィールド名のフィールドタイプ接頭辞を表す文字列(「sku_」など)に、ショッピングカートの ItemID 番号を表す変数 i(これはループのインデックス変数として使用されています)を連結しています。 |