ヘッダーをスキップ
Oracle Application Development Frameworkケース・マニュアル
10gリリース(10.1.2) 
B25064-01
  目次
目次

戻る
戻る
次へ
次へ
 

7 レッスン4: 買い物かごの注文の処理

次の項目について説明します。

7.1 概要

レッスン3で説明したように、買い物かごに商品を加える機能を追加したので、次にページ・フローを拡張して顧客の発送情報を受け取り、注文の確認を促す機能を追加します。このために、次に示す2つのJSPページの構造を調べます。

このレッスンでは、これらの2つの新しいJSPページのレイアウトについて調べます。

7.2 Proceed to Checkoutページのフロー分析

ページ・フローは、/yourcartデータ・ページからreviewcheckoutアクション・フォワードに進みます。フォワード先は/reviewcheckoutデータ・ページです。/reviewcheckoutデータ・ページには顧客が自分の最終的な注文をプレビューするための機能のみがあります。このページから可能である唯一のイベントはconfirmshippinginfoであり、これは次のフォワードへとマップし、confirmshippinginfo.jspページを表示します。それぞれのタスク用の新しいデータ・ページを含むページ・フローを図7-1「Proceed to Checkoutページのフロー」に示します。

図7-1 Proceed to Checkoutページのフロー

このイメージは、レッスン4で説明するページ・フローを示しています。

confirmshippinginfo.doアクションへのアクション・マッピングについては次のレッスンで、フロー・ダイアグラムのターゲット・ページとあわせて説明します。

実際には、ページ・フロー・モデラーを使用して、ほぼ次のように進めます。

  1. フローのページおよびアクションを識別します。

  2. フローのイベント(およびアクション・フォワード)を識別します。

  3. コンポーネント・パレットを使用して、Strutsページ・フロー・モデラーのダイアグラムの領域に、ページ・ソースおよびターゲットをドロップします。

  4. コンポーネント・パレットを使用して、アクション・フォワード要素(またはページ・リンク)をドロップし、ソースとターゲット要素を結合します。

7.3 Review Checkoutページのレイアウト

実行時にyourcart.jspページから起動されたreviewcheckoutフォワードが、/reviewcheckoutデータ・ページへのマッピングを実行します。この場合、このページに対応するデータ・アクションはモデルの初期化を行いません。ページで表示されるデータを準備するためにモデルの初期化が必要であった前述のページとは異なり、reviewcheckout.jspページは既存のモデル・オブジェクトにのみアクセスします。このページの目的は、既存のShoppingCartオブジェクトからデータを引き出し、簡潔で読みやすい表として示すことのみです。

具体的には、reviewcheckout.jspページは<c:forEach>タグを使用して、ShoppingCartIteratorバインディングを通してShoppingCartオブジェクトにバインドされたOracle ADF表バインディングを反復処理します。表の中の行のデータは、表バインディングの現在のRowから入力を受け取る様々なタグを使用して表示されます。

現在行のために表示される属性がデータをレンダリングするためのフォーマッタを使用しない場合、標準の<c:out>タグを次のように使用します。

<td><c:out value="${Row.Itemid}" /></td>
<td><c:out value="${Row.Name}" /></td>
...
<td align="center"><c:out value="${Row.Quantity}" /></td>

ビジネス・コンポーネントの中で定義された、言語依存のフォーマット・マスクによって属性が表示される場合、Oracle ADFの<adf:render>タグを使用します。

<td align="right"><adf:render model="Row.Listprice" /></td>
<td align="right"><adf:render model="Row.ExtendedTotal" /></td>

最後に、InStock属性用のYまたはNなどの1文字のフラグ値に基づいた翻訳可能な文字列をレンダリングする場合、次のようにStrutsの<bean:message>タグを使用します。

<td>
   <c:set var="inStockMsgKey" value="cart.instock.${Row.InStock}"/>
   <bean:message name="inStockMsgKey"/>
</td>

ShoppingCartオブジェクトはInStockという名前の簡単な一時フィールドを持っており、Y(はい、在庫があります)かN(いいえ、在庫はありません)のどちらかの値を取り、アイテムの在庫があるかどうかを示します。reviewcheckout.jspページがInStock情報を表示する場合、YやNの値をそのまま示すより、Strutsのメッセージ・リソース・ファイルの中の翻訳可能な文字列用の文字列のキーの名前の一部としてYやNを使用します。前述のコードでは最初に、JSTLの<c:set>タグを使用してinStockMsgKeyという名前のローカル・ページ変数に、<c:forEach>ループの現在のRowにおけるInStockフィールドの値と連結したcart.instockの値を設定します。その後、<bean:message>を使用して、そのinStockMsgKeyオブジェクトの中のcart.instock.Yメッセージ・キーの値またはcart.instock.Nメッセージ・キーの値のどちらかに基づいて翻訳された文字列を表示します。このようにして、ブラウザの優先設定に指定された言語で、在庫についてのわかりやすいインジケータをユーザーに表示することができます。たとえば、Yを英語ではIn Stockと表示し、イタリア語ではIn Magazzinoと表示することが可能です。

表の最後の行は、<bean:write format="$0.00">タグを使用して、2つの値を持つCartTotal属性を表示します。

<bean:write format="$0.00" name="CartTotal"/>

注意: 小数の書式である$0.00#,##0.00は同等で、通貨の値を表すためにどちらを使用してもかまいません。

次に示すように、データ・アクションにおいてinvokeCustomMethod()が実行されると、CartTotalの値がページで利用できるようになります。

protected void invokeCustomMethod(DataActionContext ctx) {
     Double cartTotal = getToyStoreService(ctx).getCartTotal();
     ctx.getHttpServletRequest().setAttribute("CartTotal", cartTotal);
}

このformat属性を使用した<bean:write>タグの使用方法はListprice属性値およびExtendedTotal属性値で説明した<adf:render>のアプローチにかわる方法を示しています。<adf:render>を使用する場合は、フォーマット・マスクはモデル・レイヤーのOracle ADFビジネス・オブジェクトの属性へのヒントとして提供されます。<bean:write>を使用する場合は、フォーマット・マスクはページの中にハードコードされます(あるいは、<bean:write> formatKey属性を使用して、翻訳可能なメッセージ・キーとして指定されます)。

顧客は注文に満足したら、confirmshippinginfoフォワードをトリガーしてチェックアウトします。次のように、「Continue」ボタンはconfirmshippinginfoイベントを送信します。

<a href="<c:url value='reviewcheckout.do?event=confirmshippinginfo'/>"
             ><img src="<bean:message key='images.buttons.continue'/>"
                    alt="Continue" border="0"></a>

レッスン3で説明したように、ページ・フロー・ダイアグラムに示されているconfirmshippinginfoアクション・フォワードとイベントは、再び同じ名前を共有します。データ・アクションは明示的にアクション・フォワードを設定しませんが、Oracle ADFライフサイクルは自動的に一致するフォワードを検索し、confirmshippinginfoを見つけて宣言型のナビゲーションを実行します。

7.4 Confirm Shipping Informationページのレイアウト

confirmshippinginfo.jspページの設計上の主な特徴は、フォームに値を移入し、ユーザーの入力を受け入れるために、Strutsフォーム(<html:form>タグ)を使用することです。フォームのフィールドもまたStrutsのHTML要素であり、次のようなフォームBeanのプロパティにアクセスします。

<html:select>タグと<html:optionsCollection>タグは、ページのポップリストに値を移入するためにOracle ADFのリスト・バインディングとともに動作します。<html:select>タグはフォームBeanのプロパティにバインドされており、リスト・バインディング・オブジェクト(CardtypeおよびExprYear)と名前を共有します。<html:optionsCollection>タグは、ネストされてリストの値が指定されたOracle ADFリスト・バインディングのdisplayDataプロパティからデータを取得します。これらの表示データ・コレクションのBeanはそれぞれpromptおよびindexプロパティを持っており、これらを使用してリストのそれぞれのオプションのラベルと値を(それぞれ)表示します。このサンプルの中で、Cardtype Beanとバインディング・オブジェクトに対応するクレジット・カードのタイプの選択リストを次のように表示します。

<html:select property="Cardtype" >
       <html:optionsCollection label="prompt" value="index"
                               property="Cardtype.displayData" />
</html:select>

注意:

帯域幅の最適化のために、Oracle ADFのバインディング・レイヤーはOracle ADFのリスト・バインディングの非表示の値を、displayDataコレクションの中のゼロベースのindexの番号にすることを要求します。Oracle ADFリスト・バインディングは、バインディング値の読取りおよび書込みにおいて、国コードを表すITのような内在する値のリストを、86のような索引位置に変換する処理を行います。

Oracle ADFでは、HTMLフォームは関連するアクションに結び付けられており、それはデータ・フォームBeanに結び付けられています。実行時に、HTMLフォームは次のようにデータ・フォームBeanを使用してOracle ADFバインディング・オブジェクトに対応するプロパティを決定し、それに従って使用可能なフォームの属性値を表示します。

  1. 最初に、データ・フォームBeanはOracle ADFバインディング・コンテナ(BindingContainerActionForm)にフォームの属性と一致する名前のバインディングを持っているかどうかを問い合せます。

  2. 次に、Oracle ADFバインディング・コンテナは、そのバインディングを(存在する場合)返します。そして最後に、データ・フォームBeanはそのバインディング値をHTMLフォーム属性に移入します。

同様に、ユーザーが「Continue」ボタンをクリックしてデータとともにフォームを送信すると、それによってplaceOrderイベントが発行され、Oracle ADFバインディング・コンテナはStrutsが設定したフォームの属性値を収集します。そして、ライフサイクルのprocessUpdateModel()フェーズにおいて、バインディング・コンテナはこれらの値を使用して一致する名前のOracle ADFバインディング・オブジェクトを更新します。

しかし、このページが再びレンダリングされた時にユーザーがフォームを送信し、モデル・レイヤーで検証エラーがスローされた場合、<html:errors>タグは属性に関連するエラーがフィールドの横に表示されることを保証します。


注意:

モデル・レイヤーのユーザー・アカウントを表すOracle ADFビジネス・オブジェクトは、国と州の組合せを検証するためにカスタム・ビジネス・ルールを再利用したり、主キーの属性の一意性を強制するために組み込まれた検証規則を使用したり、Emailのカスタム・データ型を使用して電子メール・アドレスの正しい書式を検証したりして、必須属性を宣言的に強制しています。すべてのカスタム・エラー・メッセージは、現在のブラウザのユーザーの、言語と領域に基づいたロケールにローカライズされます。この動作には、調整のために開発者がコードを記述する必要はありません。


ベストプラクティスのヒント:

デフォルトでは、Oracle ADFデータ・アクションはリクエスト処理のライフサイクルの間に発生したすべての例外をバンドルし、Strutsレイヤーへのリクエストの最後に(ライフサイクルのreportErrors()フェーズにおいて)StrutsのActionErrorオブジェクトに変換します。このことは、ライフサイクルの処理の間に発生したビジネス検証エラーが、ページの中の<html:errors>タグが置かれた場所に正しく表示されることを意味しています。しかし、<html:errors>タグをページに入れないと、エラーはStrutsレイヤーに報告されますが、決して表示されることはありません。このことは、予期しないエラーでさえ、<html:errors>タグを使用して明示的にStrutsエラーにレンダリングした場合にのみ表示されることを意味します。JDeveloperのデータ・コントロール・パレットを使用する場合、<html:errors>タグがページに含まれますが、手動でページを開発した場合はこれらのタグを自分で追加する必要があることに注意してください。

フォームには、ユーザーが連続して複数回同じフォームを送信しようとしたかどうかをOracle ADFコントローラ・レイヤーが検出するために使用する、次のような標準的な非表示のフィールドが含まれています。

<input type="hidden" name="<c:out value='${bindings.statetokenid}'/>
       "value="<c:out value='${bindings.statetoken}'/>"/>

このトークンは、ユーザーがページを行き来した場合、データ・アクションが同じリクエストを複数回処理することを防ぎます。

このレッスンに示したコンセプトをより深く理解するために、次の実践を任意で行ってください。

7.5 レッスン4の実践

次の実践では、データバインドされた属性のカスタム検証を行うために属性のデータ型を特定する目的で、Oracle ADF Business Componentsが検証ドメインの使用をどのようにサポートしているかを示します。

  1. アプリケーション・ナビゲータで、toystore.model.datatypesを見つけて右クリックし、「新規ドメイン」を選択します。

  2. ドメインの作成ウィザードで、「次へ」をクリックして、ドメイン名としてPhoneと入力します。他のオプションはすべて変更せずに、「終了」をクリックします。

  3. アプリケーション・ナビゲータで、新しいドメインPhoneNumberを右クリックし、「ドメイン・クラスに移動」を選択して、変更するPhone.javaテンプレートを開きます。

  4. Phone.javaのソースを開いて、スタブであるvalidate()メソッドを次の検証コードに置き換えます。

    protected void validate() {
        if (!isEightCharacters()) {
          throw new DataCreationException(ErrorMessages.class,
            ErrorMessages.INVALID_PHONENUMBER, null, null);
        }
      }
      private boolean isEightCharacters() {
        if (mData != null) {
          if (mData.length() != 8) {
            return false;
          } else {
            return true;
          }
        }
        return false;
     }
    
    
  5. インポート・リストに次のimport文を追加します。

    import oracle.jbo.domain.DataCreationException;
    
    
  6. ファイルに他の変更を加えずに、「ファイル」「保存」を選択します。

  7. アプリケーション・ナビゲータで、toystore.model.datatypes.commonを開き、ErrorMessages.javaをダブルクリックして、Phone.javaに指定されているINVALID_PHONENUMBER用のエラー・メッセージを追加します。

  8. ErrorMessages.javaのソースを開いて、次のように定数リストにこの宣言を追加します。

    public static final String INVALID_PHONENUMBER = "20005";
    
    
  9. エラー・メッセージのリストに次の内容を追加します。

    { INVALID_PHONENUMBER, "Phone number must be eight characters including dash." },
    
    
  10. アプリケーション・ナビゲータで、toystore.model.dataaccessを開き、Accountsを右クリックして「Accountsの編集」を選択します。

  11. ビュー・オブジェクト・エディタで「属性」を選択し、使用可能な属性および選択した属性のリストを表示します。「選択済の属性」リストからPhoneを選択し、「削除」矢印ボタンをクリックして「使用可能な属性」リストに戻します。この手順は、新しいドメイン・タイプを適用する前に、この属性へのビュー・オブジェクトの依存性を削除するために必要です。「OK」をクリックして変更を保存します。

  12. アプリケーション・ナビゲータで、toystore.model.businessobjectsを開き、Accountを右クリックして「Accountの編集」を選択します。

  13. エンティティ・オブジェクト・エディタで、「属性」を開き、リストからPhoneを選択します。

  14. 「エンティティ属性」タブで「タイプ」ドロップダウン・リストを表示し、toystore.model.datatypes.common.Phoneを選択します。新しいデータ型が表示されない場合は、リストから「ドメインのインポート」を選択してドメインを検索することもできます。「OK」をクリックして変更を保存し、エディタを終了します。

  15. アプリケーション・ナビゲータで、toystore.model.dataaccessを開き、Accountsを右クリックして「Accountsの編集」を選択します。

  16. ビュー・オブジェクト・エディタで「属性」を選択し、使用可能な属性および選択した属性のリストを表示します。「使用可能な属性」リストからPhoneを選択し、「追加」矢印ボタンをクリックして「選択済の属性」リストに戻します。「OK」をクリックして変更を保存し、エディタを終了します。

  17. home.doアクションを右クリックして「実行」を選択し、アプリケーションを起動します。ホームページの任意のカテゴリをクリックします。任意の商品のリンクを選択して買い物かごに追加します。チェックアウトに進みます。サインインを要求されたら、かわりに「Register as a New User」リンクをクリックしてください。フォーム入力を完成して、故意に短い電話番号を入力します。「Submit」をクリックします。データ・アクションは、電話番号フィールド用に作成した検証エラーとともにページを再び表示します。

検証ドメインは、検証ドメインをそのタイプとして使用するすべての属性にビジネス・ロジックを追加します。ドメイン検証はそのドメイン・タイプのオブジェクトが作成されると発生します。それによって、データ・オブジェクトを、再構築や再検証の必要なく、層と層の間で渡すことが可能になります。ドメインレベルの検証を実装するには、新しいドメイン・タイプを作成し、validate()メソッドにコードを追加します。ドメインが作成されると、属性のタイプとして割り当てることができます。