今回は前回に引き続いてアプリケーションへのデータアクセスの組み込みを行っていきます。
データアクセスコード配置先の検討とフローの変更-エントリー一覧フロー
前回は新規エントリー入力フローに対するデータアクセスコードの実装を行いました。新規エントリー入力フローで新規エントリーを作成するとエントリー一覧画面に遷移しますが、
前回書いたように、
では、
firstState: ProcessFindList
viewState:
- name: DisplayList
view: List
activity:
class: Entry_ListAction
method: doActivityOnDisplayList
actionState:
- name: ProcessFindList
activity:
class: Entry_ListAction
method: doActivityOnProcessFindList
transition:
- event: DisplayListFromProcessFindList
nextState: DisplayList
データアクセスコードの実装-エントリー一覧フロー
次にアクションクラスにイベントハンドラを追加し、
<?php
require_once 'Piece/Unity/Service/FlowAction.php';
require_once 'Piece/Unity/Service/FlexyElement.php';
require_once 'Piece/ORM.php';
class Entry_ListAction extends Piece_Unity_Service_FlowAction
{
var $_entries;
function doActivityOnDisplayList()
{
$viewElement = &$this->_context->getViewElement();
$viewElement->setElementByRef('entries', $this->_entries);
}
function doActivityOnProcessFindList()
{
$mapper = &Piece_ORM::getMapper('Entry');
$this->_entries = $mapper->findAll();
return 'DisplayListFromProcessFindList';
}
}
?>
作業が完了したら、


$ select * from entry; id | title | content ----+-----------+--------- 1 | タイトル1 | 内容1\r 2 | タイトル2 | 内容2\r 3 | タイトル3 | 内容3\r (3 rows)
データアクセスコード配置先の検討とフローの変更-エントリー編集フロー
これまでに新規エントリー入力フローとエントリー一覧フローの実装が完了しましたので残るはエントリー編集フローのみとなります。このフローは他のフローと比べると少々複雑ですので少しずつ実装を進めていくことにします。まずは現状のフロー定義を確認しましょう。
firstState: DisplayShow
lastState:
name: DisplayDeleteFinish
view: http://example.org/list.php
viewState:
- name: DisplayShow
view: Show
activity:
class: Entry_EditAction
method: doActivityOnDisplayShow
transition:
- event: DisplayEditFromDisplayShow
nextState: DisplayEdit
- event: DisplayDeleteConfirmViaDisplayShowFromDisplayShow
nextState: DisplayDeleteConfirmViaDisplayShow
- name: DisplayEdit
view: Edit
activity:
class: Entry_EditAction
method: doActivityOnDisplayEdit
transition:
- event: ProcessValidateEditFromDisplayEdit
nextState: ProcessValidateEdit
- event: DisplayDeleteConfirmViaDisplayEditFromDisplayEdit
nextState: DisplayDeleteConfirmViaDisplayEdit
- name: DisplayEditConfirm
view: EditConfirm
activity:
class: Entry_EditAction
method: doActivityOnDisplayEditConfirm
transition:
- event: DisplayShowFromDisplayEditConfirm
nextState: DisplayShow
- event: DisplayEditFromDisplayEditConfirm
nextState: DisplayEdit
- name: DisplayDeleteConfirmViaDisplayShow
view: DeleteConfirmViaDisplayShow
activity:
class: Entry_EditAction
method: doActivityOnDisplayDeleteConfirmViaDisplayShow
transition:
- event: DisplayDeleteFinishFromDisplayDeleteConfirmViaDisplayShow
nextState: DisplayDeleteFinish
- event: DisplayShowFromDisplayDeleteConfirmViaDisplayShow
nextState: DisplayShow
- name: DisplayDeleteConfirmViaDisplayEdit
view: DeleteConfirmViaDisplayEdit
activity:
class: Entry_EditAction
method: doActivityOnDisplayDeleteConfirmViaDisplayEdit
transition:
- event: DisplayDeleteFinishFromDisplayDeleteConfirmViaDisplayEdit
nextState: DisplayDeleteFinish
- event: DisplayEditFromDisplayDeleteConfirmViaDisplayEdit
nextState: DisplayEdit
actionState:
- name: ProcessValidateEdit
activity:
class: Entry_EditAction
method: doActivityOnProcessValidateEdit
transition:
- event: DisplayEditConfirmFromProcessValidateEdit
nextState: DisplayEditConfirm
- event: DisplayEditFromProcessValidateEdit
nextState: DisplayEdit

このフローのうちデータアクセスが必要な部分は下記のようになります。
- フロー実行開始直後のエントリー参照画面表示前(SELECT)
- エントリー編集確認画面のUpdateボタンがクリックされた後(UPDATE)
- 各エントリー削除確認画面のDeleteボタンがクリックされた後(DELETE)
データアクセスコードの実装:エントリーの検索-エントリー編集フロー
最初に
ステップ1-データアクセスコードの実装
まずは新規にアクションステートを追加します。
firstState: ProcessFind
...
actionState:
- name: ProcessValidateEdit
activity:
class: Entry_EditAction
method: doActivityOnProcessValidateEdit
transition:
- event: DisplayEditConfirmFromProcessValidateEdit
nextState: DisplayEditConfirm
- event: DisplayEditFromProcessValidateEdit
nextState: DisplayEdit
- name: ProcessFind
activity:
class: Entry_EditAction
method: doActivityOnProcessFind
transition:
- event: DisplayShowFromProcessFind
nextState: DisplayShow
検索処理を行うためのアクションステートProcessFindを追加し、
<?php
require_once 'Piece/Unity/Service/FlowAction.php';
require_once 'Piece/Unity/Service/FlexyElement.php';
class Entry_EditAction extends Piece_Unity_Service_FlowAction
{
...
function doActivityOnDisplayDeleteConfirmViaDisplayEdit()
{
$flexyElement = &new Piece_Unity_Service_FlexyElement();
$flexyElement->addForm($this->_flow->getView(), $this->_context->getScriptName());
$viewElement = &$this->_context->getViewElement();
$viewElement->setElementByRef('entry', $this->_entry);
}
function doActivityOnProcessFind()
{
$request = &$this->_context->getRequest();
$mapper = &Piece_ORM::getMapper('Entry');
$this->_entry = &$mapper->findById($request->getParameter('id'));
return 'DisplayShowFromProcessFind';
}
}
...
アクションステートProcessFindのアクティビティに設定されたイベントハンドラdoActivityOnProcessFind()を追加しています。また、
作業が完了したら、
ステップ2-idフィールドのバリデーション
しかしまだ問題があります。現在のコードはリクエストパラメータに含まれるidフィールドの値を直接参照しており、
...
function doActivityOnProcessFind()
{
$validation = &$this->_context->getValidation();
if ($validation->validate('ID', $this->_entry)) {
$request = &$this->_context->getRequest();
$mapper = &Piece_ORM::getMapper('Entry');
$this->_entry = &$mapper->findById($request->getParameter('id'));
return 'DisplayShowFromProcessFind';
} else {
return 'DisplayShowFromProcessFind';
}
}
...
$validation->validate()に渡されたバリデーションセット"ID"に対応するバリデーション定義ファイルも用意します。
- name: id
required:
validator:
- name: Range
rule:
min: 1
max: 2147483647
作業が完了したら、
ステップ3-エラー画面の作成
バリデーションが失敗した場合の動作は確認できましたが、
...
- name: DisplayError
view: Error
actionState:
- name: ProcessValidateEdit
activity:
class: Entry_EditAction
method: doActivityOnProcessValidateEdit
transition:
- event: DisplayEditConfirmFromProcessValidateEdit
nextState: DisplayEditConfirm
- event: DisplayEditFromProcessValidateEdit
nextState: DisplayEdit
- name: ProcessFind
activity:
class: Entry_EditAction
method: doActivityOnProcessFind
transition:
- event: DisplayShowFromProcessFind
nextState: DisplayShow
- event: DisplayErrorFromProcessFind
nextState: DisplayError
...
function doActivityOnProcessFind()
{
$validation = &$this->_context->getValidation();
if ($validation->validate('ID', $this->_entry)) {
$mapper = &Piece_ORM::getMapper('Entry');
$this->_entry = &$mapper->findById($this->_entry);
return 'DisplayShowFromProcessFind';
} else {
return 'DisplayErrorFromProcessFind';
}
}
...
新たに遷移イベントDisplayErrorFromProcessFindを追加し、
<h3>Error occured!</h3>
作業が完了したら、

ステップ4-検索結果による分岐
これでidフィールドのバリデーションは解決しましたが、
...
function doActivityOnProcessFind()
{
$validation = &$this->_context->getValidation();
if ($validation->validate('ID', $this->_entry)) {
$mapper = &Piece_ORM::getMapper('Entry');
$entry = &$mapper->findById($this->_entry);
if (!is_null($entry)) {
$this->_entry = &$entry;
return 'DisplayShowFromProcessFind';
} else {
return 'DisplayErrorFromProcessFind';
}
} else {
return 'DisplayErrorFromProcessFind';
}
}
...
$mapper->findById()のようなfindByから始まるメソッドは該当するレコードが見つからない場合にnullを返しますので、
作業が完了したら、
データアクセスコードの実装:エントリーの更新-エントリー編集フロー
次に
ステップ1-データアクセスコードの実装
検索の場合と同様にまずはデータアクセスコードの実装を行います。下記はアクションステート追加後のフロー定義ファイルの内容です。
...
- name: DisplayEditConfirm
view: EditConfirm
activity:
class: Entry_EditAction
method: doActivityOnDisplayEditConfirm
transition:
- event: DisplayEditFromDisplayEditConfirm
nextState: DisplayEdit
- event: ProcessUpdateFromDisplayEditConfirm
nextState: ProcessUpdate
actionState:
...
- name: ProcessUpdate
activity:
class: Entry_EditAction
method: doActivityOnProcessUpdate
transition:
- event: DisplayShowFromProcessUpdate
nextState: DisplayShow
ビューステートのイベント名を変更した場合、
<h4 class="date-header">EditConfirm</h4>
<p>Title: {entry.title}</p>
<p>Content: {GLOBALS.displayTextArea(entry.content):h}</p>
<form name="EditConfirm" id="EditConfirm">
<input type="hidden" name="{__flowExecutionTicketKey}" value="{__flowExecutionTicket}" />
<p>
<input type="submit" name="{__eventNameKey}_DisplayEditFromDisplayEditConfirm" value="Back" />
<input type="submit" name="{__eventNameKey}_ProcessUpdateFromDisplayEditConfirm" value="Update" />
</p>
</form>
アクションクラスにイベントハンドラを追加し、
...
function doActivityOnProcessUpdate()
{
$mapper = &Piece_ORM::getMapper('Entry');
$mapper->update($this->_entry);
return 'DisplayShowFromProcessUpdate';
}
...
作業が完了したら、
ステップ2-更新結果による分岐
次に検索の場合と同様にデータベースクエリ実行結果による分岐を組み込みます。更新された件数が0件ならエラー画面に遷移するようにしましょう。
...
- name: ProcessUpdate
activity:
class: Entry_EditAction
method: doActivityOnProcessUpdate
transition:
- event: DisplayShowFromProcessUpdate
nextState: DisplayShow
- event: DisplayErrorFromProcessUpdate
nextState: DisplayError
...
function doActivityOnProcessUpdate()
{
$mapper = &Piece_ORM::getMapper('Entry');
if ($mapper->update($this->_entry)) {
return 'DisplayShowFromProcessUpdate';
} else {
return 'DisplayErrorFromProcessUpdate';
}
}
...
上記のようにエラー画面に遷移するためのイベントを追加し、
ステップ3-更新後の再検索
これでエントリーの更新は問題なく行えるようになりましたが、
CREATE TABLE entry (
id serial,
title varchar(255) NOT NULL,
content text NOT NULL,
rdate timestamp with time zone NOT NULL DEFAULT current_timestamp,
mdate timestamp with time zone NOT NULL DEFAULT current_timestamp,
PRIMARY KEY(id)
);
テーブルの再作成が完了したら、
次にエントリー参照画面のHTMLテンプレートに登録日及び更新日を追加します。
<h4 class="date-header">Show</h4>
<p>Title: {entry.title}</p>
<p>Content: {GLOBALS.displayTextArea(entry.content):h}</p>
<p>Rdate: {entry.rdate}</p>
<p>Mdate: {entry.mdate}</p>
<form name="Show" id="Show">
<input type="hidden" name="{__flowExecutionTicketKey}" value="{__flowExecutionTicket}" />
<p>
<input type="submit" name="{__eventNameKey}_DisplayEditFromDisplayShow" value="Edit" />
<input type="submit" name="{__eventNameKey}_DisplayDeleteConfirmViaDisplayShowFromDisplayShow" value="Delete" />
</p>
</form>
では、

次に、
Piece_
さて、
- name: update
query: UPDATE entry SET title = $title, content = $content, mdate = current_timestamp WHERE id = $id
マッパー定義ファイルの準備ができたら、
これでようやく問題の可視化ができました。この問題はエントリー更新後に再度対象レコードを検索することで解決できます。すなわち、
...
- name: ProcessUpdate
activity:
class: Entry_EditAction
method: doActivityOnProcessUpdate
transition:
- event: ProcessFindFromProcessUpdate
nextState: ProcessFind
- event: DisplayErrorFromProcessUpdate
nextState: DisplayError
アクションステートのイベント名を変更した場合、
...
function doActivityOnProcessUpdate()
{
$mapper = &Piece_ORM::getMapper('Entry');
if ($mapper->update($this->_entry)) {
return 'ProcessFindFromProcessUpdate';
} else {
return 'DisplayErrorFromProcessUpdate';
}
}
...
ここでブラウザから動作を確認したところ、
この問題に対してはバリデーション部分の抽出による対処を行います。変更後のフロー定義ファイル及びアクションクラスを下記に示します。
firstState: ProcessValidateID
...
actionState:
...
- name: ProcessUpdate
activity:
class: Entry_EditAction
method: doActivityOnProcessUpdate
transition:
- event: ProcessFindFromProcessUpdate
nextState: ProcessFind
- event: DisplayErrorFromProcessUpdate
nextState: DisplayError
- name: ProcessValidateID
activity:
class: Entry_EditAction
method: doActivityOnProcessValidateID
transition:
- event: ProcessFindFromProcessValidateID
nextState: ProcessFind
- event: DisplayErrorFromProcessValidateID
nextState: DisplayError
...
function doActivityOnProcessFind()
{
$mapper = &Piece_ORM::getMapper('Entry');
$entry = &$mapper->findById($this->_entry);
if (!is_null($entry)) {
$this->_entry = &$entry;
return 'DisplayShowFromProcessFind';
} else {
return 'DisplayErrorFromProcessFind';
}
}
function doActivityOnProcessUpdate()
{
$mapper = &Piece_ORM::getMapper('Entry');
if ($mapper->update($this->_entry)) {
return 'ProcessFindFromProcessUpdate';
} else {
return 'DisplayErrorFromProcessUpdate';
}
}
function doActivityOnProcessValidateID()
{
$validation = &$this->_context->getValidation();
if ($validation->validate('ID', $this->_entry)) {
return 'ProcessFindFromProcessValidateID';
} else {
return 'DisplayErrorFromProcessValidateID';
}
}
...
作業が完了したら、

データアクセスコードの実装:エントリーの削除-エントリー編集フロー
最後に
検索の場合と同様にまずはデータアクセスコードの実装を行います。下記はアクションステート追加後のフロー定義ファイルの内容になります。今回はこの時点でエラー画面への遷移も組み込みます。
...
- name: DisplayDeleteConfirmViaDisplayShow
view: DeleteConfirmViaDisplayShow
activity:
class: Entry_EditAction
method: doActivityOnDisplayDeleteConfirmViaDisplayShow
transition:
- event: ProcessDeleteFromDisplayDeleteConfirmViaDisplayShow
nextState: ProcessDelete
- event: DisplayShowFromDisplayDeleteConfirmViaDisplayShow
nextState: DisplayShow
- name: DisplayDeleteConfirmViaDisplayEdit
view: DeleteConfirmViaDisplayEdit
activity:
class: Entry_EditAction
method: doActivityOnDisplayDeleteConfirmViaDisplayEdit
transition:
- event: ProcessDeleteFromDisplayDeleteConfirmViaDisplayEdit
nextState: ProcessDelete
- event: DisplayEditFromDisplayDeleteConfirmViaDisplayEdit
nextState: DisplayEdit
- name: DisplayError
view: Error
actionState:
...
- name: ProcessDelete
activity:
class: Entry_EditAction
method: doActivityOnProcessDelete
transition:
- event: DisplayDeleteFinishFromProcessDelete
nextState: DisplayDeleteFinish
- event: DisplayErrorFromProcessDelete
nextState: DisplayError
前述のように、
<h4 class="date-header">DeleteConfirmViaDisplayShow</h4>
<p>Title: {entry.title}</p>
<p>Content: {GLOBALS.displayTextArea(entry.content):h}</p>
<form name="Show" id="Show">
<input type="hidden" name="{__flowExecutionTicketKey}" value="{__flowExecutionTicket}" />
<p>
<input type="submit" name="{__eventNameKey}_DisplayShowFromDisplayDeleteConfirmViaDisplayShow" value="Back" />
<input type="submit" name="{__eventNameKey}_ProcessDeleteFromDisplayDeleteConfirmViaDisplayShow" value="Delete" />
</p>
</form>
<h4 class="date-header">DeleteConfirmViaDisplayEdit</h4>
<p>Title: {entry.title}</p>
<p>Content: {GLOBALS.displayTextArea(entry.content):h}</p>
<form name="Show" id="Show">
<input type="hidden" name="{__flowExecutionTicketKey}" value="{__flowExecutionTicket}" />
<p>
<input type="submit" name="{__eventNameKey}_DisplayEditFromDisplayDeleteConfirmViaDisplayEdit" value="Back" />
<input type="submit" name="{__eventNameKey}_ProcessDeleteFromDisplayDeleteConfirmViaDisplayEdit" value="Delete" />
</p>
</form>
アクションクラスにイベントハンドラを追加し、
...
function doActivityOnProcessDelete()
{
$mapper = &Piece_ORM::getMapper('Entry');
if ($mapper->delete($this->_entry)) {
return 'DisplayDeleteFinishFromProcessDelete';
} else {
return 'DisplayErrorFromProcessDelete';
}
}
...
作業が完了したら、



エントリー編集フローの最終的なステートチャート図は下記のようになります。

おわりに
今回はエントリー一覧フロー及びエントリー編集フローにデータアクセスコードを組み込みました。単にデータアクセスコードが実装されるだけでなく、
エントリー編集フローの最終的なステートチャート図を見ていただくとわかるように、
Piece_

次回は、