今回は前回に引き続いてアプリケーションへのデータアクセスの組み込みを行っていきます。
データアクセスコード配置先の検討とフローの変更-エントリー一覧フロー
前回は新規エントリー入力フローに対するデータアクセスコードの実装を行いました。新規エントリー入力フローで新規エントリーを作成するとエントリー一覧画面に遷移しますが、エントリー一覧画面で表示される内容はモックオブジェクトのものであり本物ではありません。そこで次はエントリー一覧フローにデータアクセスコードを組み込み、実際のレコードからエントリーの一覧が作成されるようにします。まずは新規エントリー入力フローと同様に、コードの配置先について検討してみます。
前回書いたように、筆者はビュー関連以外のコードをアクションステートのアクティビティ(ステート更新時のイベントハンドラに対応)に配置することを推奨していますので、今回もそれに従って新規にアクションステートを用意し、そのステートのアクティビティにデータアクセスコードを配置することにします。
では、下記のようにフロー定義ファイルを変更しましょう。
データアクセスコードの実装-エントリー一覧フロー
次にアクションクラスにイベントハンドラを追加し、データアクセスコードを記述します。ここでは単にentryテーブルの全レコードを取得するために$mapper->findAll()を使うことにします。また、モックオブジェクトの設定コードが不要になったためコンストラクタを削除しておきます。
作業が完了したら、ブラウザから動作を確認してください。エラーが発生せずにデータベースの内容が反映されたエントリー一覧画面が表示されたら成功です。また、新規エントリー入力フローからエントリーを追加してみましょう。きちんと新しいレコードが画面に反映されるはずです。また、直接データベースにアクセスし、表示されているレコードがデータベース由来のものか確認しておきましょう。
データアクセスコード配置先の検討とフローの変更-エントリー編集フロー
これまでに新規エントリー入力フローとエントリー一覧フローの実装が完了しましたので残るはエントリー編集フローのみとなります。このフローは他のフローと比べると少々複雑ですので少しずつ実装を進めていくことにします。まずは現状のフロー定義を確認しましょう。
このフローのうちデータアクセスが必要な部分は下記のようになります。
- フロー実行開始直後のエントリー参照画面表示前(SELECT)
- エントリー編集確認画面のUpdateボタンがクリックされた後(UPDATE)
- 各エントリー削除確認画面のDeleteボタンがクリックされた後(DELETE)
データアクセスコードの実装:エントリーの検索-エントリー編集フロー
最初に「フロー実行開始直後のエントリー参照画面表示前(SELECT)」該当部分にデータアクセスを行うためのアクションステートを追加し、イベントハンドラの実装を行います。
ステップ1-データアクセスコードの実装
まずは新規にアクションステートを追加します。
検索処理を行うためのアクションステートProcessFindを追加し、ファーストステートに設定しています。次にアクションクラスを見てみましょう。
アクションステートProcessFindのアクティビティに設定されたイベントハンドラdoActivityOnProcessFind()を追加しています。また、モックオブジェクトの設定コードが不要になったためコンストラクタを削除しています。
作業が完了したら、ブラウザから動作を確認しましょう。エントリー一覧画面のリンクをクリックすると、対象エントリーの内容が表示されるはずです。
ステップ2-idフィールドのバリデーション
しかしまだ問題があります。現在のコードはリクエストパラメータに含まれるidフィールドの値を直接参照しており、その値はバリデーションされたものではありません。アプリケーションの入力値に対するバリデーションはセキュリティリスクを低減する上で必須といえますので、きちんとidフィールドのバリデーションを行うことにしましょう。バリデーションを加えたアクションクラスは下記のようになります。
$validation->validate()に渡されたバリデーションセット"ID"に対応するバリデーション定義ファイルも用意します。
作業が完了したら、ブラウザから動作を確認しましょう。URLを編集しidフィールドの値を数値以外にすると、内容が表示されなくなるでしょう。
ステップ3-エラー画面の作成
バリデーションが失敗した場合の動作は確認できましたが、この画面はアプリケーションが作成したリンクから遷移することになるため、本来バリデーションが失敗することはないはずです。よって、失敗時にエントリー参照画面が表示されるのは適切ではありません。ここは新規にエラー画面を用意し、バリデーション失敗時はその画面に遷移するようにします。下記に変更内容を示します。
新たに遷移イベントDisplayErrorFromProcessFindを追加し、バリデーション失敗時にそのイベントを返すようにしています。また、$mapper->findById()に渡す値をリクエストパラメータのものから、$_entryプロパティに変更しています。この場合、Piece_ORMによって$_entry->idプロパティが使用されます。また、HTMLテンプレートも新規に作成します。
作業が完了したら、ブラウザから動作を確認しましょう。不正な値を与えると今度はエラー画面が表示されるはずです。
ステップ4-検索結果による分岐
これでidフィールドのバリデーションは解決しましたが、まだ問題が残っています。それは$mapper->findById()によってレコードが見つからなかった場合の対応です。idフィールドに対してデータベースに存在しない値を与えると、バリデーションの時と同じく内容が表示されなくなってしまいます。そこで、idフィールドに対してデータベースに存在しない値を受け取った場合は、先ほど作成したエラー画面に遷移するようにします。下記に変更内容を示します。
$mapper->findById()のようなfindByから始まるメソッドは該当するレコードが見つからない場合にnullを返しますので、それを利用したコードになっています。
作業が完了したら、ブラウザから動作を確認しましょう。これでデータベースに存在しない数値を与えるとエラー画面が表示されるようになりました。エントリーの検索はこれで完成です。
データアクセスコードの実装:エントリーの更新-エントリー編集フロー
次に「エントリー編集確認画面のUpdateボタンがクリックされた後(UPDATE)」該当部分にデータアクセスを行うためのアクションステートを追加し、イベントハンドラの実装を行います。
ステップ1-データアクセスコードの実装
検索の場合と同様にまずはデータアクセスコードの実装を行います。下記はアクションステート追加後のフロー定義ファイルの内容です。
ビューステートのイベント名を変更した場合、合わせてHTMLテンプレート内に埋め込まれたイベント名も変更する必要があります。
アクションクラスにイベントハンドラを追加し、レコードの更新のためのコードを実装します。
作業が完了したら、ブラウザから動作を確認してみましょう。また、データベースに直接アクセスし変更が反映されているか確認してください。
ステップ2-更新結果による分岐
次に検索の場合と同様にデータベースクエリ実行結果による分岐を組み込みます。更新された件数が0件ならエラー画面に遷移するようにしましょう。
上記のようにエラー画面に遷移するためのイベントを追加し、アクションクラスのイベントハンドラで分岐を行うようにします。
ステップ3-更新後の再検索
これでエントリーの更新は問題なく行えるようになりましたが、ひとつ問題が残っています。それは、現在の実装ではSQLやデータベースサーバによって設定されるような値に対応できない、というものです。例えば、レコードの更新日付をSQLによって設定する場合がそれにあたります。UPDATE foo SET mdate = current_timestampのようなSQLが実行された場合、最新のmdateフィールドの値を得るには再度対象レコードを検索する必要があります。この問題を解決する前に、まずは問題の可視化を行います。具体的には一旦entryテーブルを削除し、登録日(rdate)及び更新日(mdate)フィールドを加えた形で再度テーブルを作成することにします。
テーブルの再作成が完了したら、Piece_ORMのキャッシュ(/path/to/pieceblog/web/webapp/cache/ormディレクトリ以下のファイル)を削除してください。
次にエントリー参照画面のHTMLテンプレートに登録日及び更新日を追加します。
では、新規エントリーをいくつか追加し、エントリー参照画面を表示させてみてください。そこには下記のように登録日と更新日が反映されているでしょう。
次に、エントリーを更新するときに更新日が自動的に設定されるようにします。
Piece_ORMはテーブルのメタデータに基づき、いくつかのメソッドと対応するSQLを自動生成します。それらの中にはSQLのINSERT, UPDATEに対応するメソッドとしてinsert(), update()も含まれますが、それらのメソッドに対応するSQLからはデフォルト値を持つフィールドが除外されるようになっています。
さて、更新日にはデフォルト値としてcurrent_timestampが設定されていますので、INSERT, UPDATEのSQLに更新日は含まれません。INSERTの方はこれで問題ありませんが、UPDATEの方はSQLを上書きしなければ更新日が更新されないことになります。よって、UPDATEのSQLはマッパー定義ファイルを使って上書きする必要があります。下記にマッパー定義ファイルの内容を示します。
マッパー定義ファイルの準備ができたら、実際にアプリケーションからエントリーの更新を行い、対象レコードのmdateフィールドの値が変更されることを確認しましょう。また、エントリー参照画面の更新日には、更新されたmdateフィールドの値が反映されないことも確認してください。
これでようやく問題の可視化ができました。この問題はエントリー更新後に再度対象レコードを検索することで解決できます。すなわち、ProcessUpdateステートからProcessFindステートへ遷移するようにフロー定義を変更することで解決できます。
アクションステートのイベント名を変更した場合、合わせてイベントハンドラから返すイベント名も変更する必要があります。
ここでブラウザから動作を確認したところ、エントリー更新後にエラー画面に遷移してしまうことがわかりました。これは、ProcessFindステートのアクティビティによるidフィールドのバリデーションが原因です。エントリー更新のタイミングではリクエストにidフィールドが含まれていないためバリデーションが失敗します。そして正常な遷移としてエラー画面が表示されるというわけです。
この問題に対してはバリデーション部分の抽出による対処を行います。変更後のフロー定義ファイル及びアクションクラスを下記に示します。
作業が完了したら、ブラウザから動作を確認しましょう。これでエントリーの更新は完成です。
データアクセスコードの実装:エントリーの削除-エントリー編集フロー
最後に「各エントリー削除確認画面のDeleteボタンがクリックされた後(DELETE)」該当部分にデータアクセスを行うためのアクションステートを追加し、イベントハンドラの実装を行います。
検索の場合と同様にまずはデータアクセスコードの実装を行います。下記はアクションステート追加後のフロー定義ファイルの内容になります。今回はこの時点でエラー画面への遷移も組み込みます。
前述のように、ビューステートのイベント名を変更した場合、合わせてHTMLテンプレート内に埋め込まれたイベント名も変更する必要があります。
アクションクラスにイベントハンドラを追加し、レコードの削除のためのコードを実装します。
作業が完了したら、ブラウザから動作を確認しましょう。これでエントリーの削除は完成です。
エントリー編集フローの最終的なステートチャート図は下記のようになります。
おわりに
今回はエントリー一覧フロー及びエントリー編集フローにデータアクセスコードを組み込みました。単にデータアクセスコードが実装されるだけでなく、バリデーションやエラーハンドリングの必要性からフローの変更が行われたことに注意してください。
エントリー編集フローの最終的なステートチャート図を見ていただくとわかるように、実装が進むとともにフローの複雑性が増加することは避けられません。しかし、今ではPiece_IDEを使うことでその複雑性に対応することができます。
Piece_IDEのフローデザイナーはGUIベースのフロー定義を可能にします。フローデザイナーを使うと複雑なフローでも混乱することなく開発を進められると思います。是非ご利用ください。
次回は、各フローを互いに接続し、ひとつのアプリケーションとしてスムースに動作するようにします。