(※このトピックスは、Valence開発元(米CNX社)のブログ記事を翻訳・再編集したものとなります。原文記事は、コチラとなります。)
ビジネスデータのリストやグリッドは、特に単一列の比較を適用する場合、非常に簡単にフィルタリングできることがよくあります。例えば、顧客からの注文のリストを特定の国や出荷日に限定する必要がありますか?問題ありません。2つのシンプルなカラムフィルター(1つは出荷先国、もう1つは出荷日)を使って、ユーザーは探しているものを取得することができます。
しかし、時にはもっと変わったフィルタリングのニーズがあるユーザーもいるかもしれません…。
“IT担当者さん、毎週の営業会議で、スカンジナビア諸国への顧客注文のリストが必要なんだけど。ただし、月末の金曜日に出荷され、虹色の商品が1つ以上含まれる注文に限るからね!”
このような要求を満たすには、標準的なフィルタのかなり複雑な組み合わせと、基礎となるデータソースの複雑なロジックが必要になる可能性があります。
あるいは、グリッドのヘッダーに「Show me ‘Unicorn’ orders only」チェックボックスを1つ追加し、要求されたデータ・セットの複雑なWHERE句をバックエンドのRPGプログラムに作成させることも可能でしょう。このヒントでは、単純なチェックボックスの背後に複雑なフィルターを配置する方法と、通常のユーザー・フィルターを編集してチェックする方法を紹介します。
グリッド上でカスタム・フィルターを作成する鍵は、RPGのプログラムをウィジェットにリンクすることです。 RPGプログラムは、ユーザーのフィルター値が適切であることを確認し、データ・ソースに適用される結果としてのフィルターを上書きするために使用されます。
そこで、このコンセプトを例で示すために、出荷された顧客注文のリストをユーザーに提供するグリッド・ウィジェットを作成し、RPGで書かれたバックエンド・フィルタにリンクされた特別な「ユニコーン」チェックボックスを含めてみましょう。まず、Valence に含まれるデモ注文ヘッダー・ファイル DEMOORD_H に対して、SQL ベースのデータ・ソースを作成することから始めます。データ・ソースは以下のSQLステートメントを使用します。
select * from demoord_h
where status='SHIPPED'
order by schdate, orderno
データソースの設置が完了したら、次はそれをグリッド・ウィジェットにリンクさせるのですが、これには特別な2つの要素があります。
- 既にフィルタとして使用されていない列の上にチェックボックスフィルタを追加します。
- このグリッドに適用されるWHERE句を上書きするRPGプログラムを追加する。
このデモでは、SCHDATEカラムに標準的な日付範囲制約を追加し、郵便番号カラム(SHPZIP)を魔法のチェックボックスとして使用することにします。これは、ユーザーが実際の郵便番号フィルタを必要としないことを想定しています。そうでなければ、他のカラムを選択するか、データソースにダミーのカラムを追加することができます。
そこで、ウィジェットのフィルタ・タブで、SCHDATE列を「Between(範囲一致)」フィルタとして設定し、SHPZIP列をチェックボックス列に変換します(この図では、フィルタ・テキスト・ラベルを上書きしています)。
チェックボックスのセルをクリックすると、どの条件を適用するのかを指定するよう促されることに注意してください。 チェックボックスがチェックされたときだけ特別なフィルターを適用したいので、「Checked」を選択してカスタム値を *UNICORN に上書きし、バックエンドの RPG プログラムが探すことにします。
バックエンドのフィルタープログラムについてですがこのプログラムの目的は2つあります: (1) 必要に応じてフィルター値を編集すること、そして (2) グリッドのデータソースの WHERE 句をオプションで上書きすることです。ここで、特別なチェックボックスのロジックが活躍します。
RPGプログラムはEXNABFLTのソース・コードをモデル化し、編集チェックとWHERE句のオーバーライドの「内容」はProcessプロシージャーに配置します。 最終的に、RPGプログラムは以下を含む応答をフロントエンドに送信する必要があります。
- success: true/false — “true” はフィルタに問題がないことを示し、”false” はグリッドにフィルタを適用しようとする試みを停止させます。
- msg: 成功が false の場合、ユーザーに表示したい例外メッセージをここで指定する必要があります。
- filter: 成功がtrueの場合、WHERE句のオーバーライドはここに入ります。
この例のソースコードは、次のようなものです。
** --------------------------------------------------------------
p Process b
d pi
d lZipCode s 10a
d lfrDateA s 10a
d ltoDateA s 10a
d lFilterString s 256a varying
lZipCode = GetValue('SHPZIP':'value');
lFrDateA = GetValue('SCHDATE':'value');
lToDateA = GetValue('SCHDATE':'value2');
// 日付範囲の検証...
if lToDateA<lFrDateA;
vvOut_success(*off:'FROM date must be prior to TO date');
return;
endif;
if lZipCode=*blanks and lToDateA=*blanks;
// フィルタリングするものはない
vvOut_success();
return;
endif;
// ここまで来れば、少なくとも1つのフィルタが適用されます...
if lToDateA<>*blanks;
lFilterString='SCHDATE between '+SQ+lFrDateA+SQ+
' and '+SQ+lToDateA+SQ;
endif;
if lZipCode='*UNICORN';
if lFilterString<>*blanks;
lFilterString+=' and ';
endif;
lFilterString+=' SHPSTATE in (''NY'',''NJ'',''CT'') and'+
' ORDERNO in (select ORDERNO '+
'from DEMOORD_D '+
'where ITEM like ''PCB%'')';
endif;
vvOut_success(*on:*omit:'filter':'('+lFilterString+')');
p e
ご覧のように、ユーザーがチェックボックスにチェックを入れると、RPGプログラムは当社の特別な「ユニコーン」制限を少し曖昧なものに適用します。この場合、米国のニューヨーク、ニュージャージー、コネチカット(それぞれ州コードはNY、NJ、CT)のいずれかの州に出荷され、注文の少なくとも1行がPCBから始まる商品である注文に適用されます。この「ディープフィルタリング」のコンセプトは、あなたの想像力でどこまでも進化させることができます。
余談ですが、EXNABFLTのソースには、SetValue()を使って既存のフィルタフィールドの値を上書きし、WriteAllFilters()を呼び出してgOutFilterというグローバルフィルタフィールドに変更を適用する方法が示されています。この方法は、ユーザが指定したフィルタを削除しない限りは使用できます。 しかし、今回のケースでは、郵便番号フィールドを特別な目的のために徴用しているので、SHPZIP=’*UNICORN’ という制約が WHERE 句に含まれることを望んでいません。したがって、WHERE句全体を完全に自分自身で構築し、それをフロントエンドに渡すだけです。 データソース定義で指定されたWHERE条件(この場合、STATUS=’SHIPPED’)はここでは変更されません。むしろ、このRPGプログラムで組み立てられたフィルタは、それを補足するものである点に注意してください。
最後に、コンパイルしたRPGプログラムをグリッド・ウィジェットにリンクする必要があります。 これは、Filterタブの一番上にある[フィルタ プログラム]フィールドで行います(下図参照)。
この状態で、[設定]タブに切り替えてチェックボックスをクリックすれば、すぐにRPGプログラムをテストすることができます。
こうして、思いつく限りのクレイジーなフィルターロジックをカプセル化し、シンプルなチェックボックスでエレガントに対応する方法が完成しました。