Piece Frameworkによるブログアプリケーションの作成

第7回画面設計とバリデーション定義(2)

前回は、新規エントリー入力フローの画面設計とバリデーション定義を行いました。今回は、新規エントリー入力フローと同様に、残りのふたつのフロー、エントリー一覧フローとエントリー編集フローの画面設計とバリデーション定義を行います。

表示項目の検討とアプリケーションへの反映-エントリー一覧画面

エントリー一覧フローが持つ唯一の画面であるエントリー一覧画面は、既存のエントリー一覧の参照や、任意のエントリーに対するエントリー編集フローへの遷移に使われる画面です。この画面の表示項目は、新規エントリー入力画面で準備した項目のひとつである「タイトル」のみとします。また、この画面に表示される各エントリーからは、エントリー編集フローに遷移できる必要がありますので、そのためのリンクも用意しなければいけません。以上を考慮し変更を加えたHTMLテンプレートと、変更後の画面を下記に示します。

/path/to/pieceblog/web/webapp/templates/Entry/List.html
<h4 class="date-header">List</h4>
<table border="1"> 
<tr flexy:foreach="entries,entry">
  <td><a href="{__appRootPath}/edit.php?id={entry.id}">{entry.title}</a></td>
</tr>
</table> 
エントリー一覧画面
エントリー一覧画面

画面を見て頂くとわかるように、表示される内容が変更前と何も変わっていません。このままではエントリー一覧表示、エントリー編集フローへの遷移ともに確認することができません。また、現段階ではアプリケーションとデータベースが連携していないため、本物のレコードを使った確認を行うこともできません。では、どうすればこの状況を打開できるのでしょうか?

モックオブジェクトの作成-エントリー一覧フロー

このような場合は、本物のオブジェクトのメソッドやプロパティを模倣したオブジェクト、モックオブジェクトの出番です。今回必要なモックオブジェクトは、上記のHTMLテンプレートから割り出すことができます。まず、エントリー一覧は、entriesビューエレメントとして設定されている配列であることがわかります。次に、entries配列の要素は、HTMLテンプレート内で変数entryとして設定されているオブジェクトであることがわかります。このオブジェクトには少なくともidとtitleのふたつのプロパティが必要です。ここまでわかれば、あとは適当なタイミングでいくつかのオブジェクトを作成し、それらを配列に追加し、その配列をentriesという名称のビューエレメントとして設定するだけです。モックオブジェクトの作成及び設定のコードは下記のようになります。ビューエレメントの設定をアクティビティで行うため、フロー定義ファイルにも変更が必要なことに注意してください。

/path/to/pieceblog/web/webapp/config/flows/Entry/List.yaml
firstState: DisplayList

viewState:

  - name: DisplayList
    view: List
    activity: 
      class: Entry_ListAction
      method: doActivityOnDisplayList 
/path/to/pieceblog/web/webapp/actions/Entry/ListAction.php
<?php
require_once 'Piece/Unity/Service/FlowAction.php';
require_once 'Piece/Unity/Service/FlexyElement.php';

class Entry_ListAction extends Piece_Unity_Service_FlowAction
{
    var $_entries = array();

    function Entry_ListAction()
    {
        $entry = &new stdClass();
        $entry->id = 1;
        $entry->title = 'Foo';
        $this->_entries[] = &$entry;
        $entry = &new stdClass();
        $entry->id = 2;
        $entry->title = 'Bar';
        $this->_entries[] = &$entry;
        $entry = &new stdClass();
        $entry->id = 3;
        $entry->title = 'Baz';
        $this->_entries[] = &$entry;
    }

    function doActivityOnDisplayList()
    {
        $viewElement = &$this->_context->getViewElement();
        $viewElement->setElementByRef('entries', $this->_entries);
    }
}
?>

変更が完了したら、ブラウザから画面を確認しましょう。

エントリー一覧画面
エントリー一覧画面

今度は、きちんと一覧が表示されました。これで、エントリー一覧フローの画面設計は完了です。なお、このフローには検証すべき入力項目が存在しないため、バリデーション定義は必要ありません。

表示項目の検討とアプリケーションへの反映-エントリー参照画面

続いて、エントリー編集フローに入ります。

エントリー編集フローの最初の画面であるエントリー参照画面は、指定されたエントリーの内容の参照や、エントリーの編集画面及び削除確認画面への遷移に使われる画面です。この画面の表示項目は、新規エントリー入力画面と同様に「タイトル」「内容」とします。さらに、編集画面及び削除確認画面へのリンクをフォームのsubmitボタンに変更します。また、このフローにもエントリー一覧フローと同様にモックオブジェクトが必要になりますので、それも準備しておきましょう。最初に、変更を加えたHTMLテンプレートを下記に示します。

/path/to/pieceblog/web/webapp/templates/Entry/Show.html
<h4 class="date-header">Show</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}_DisplayEditFromDisplayShow" value="Edit" />
    <input type="submit" name="{__eventNameKey}_DisplayDeleteConfirmViaDisplayShowFromDisplayShow" value="Delete" />
  </p>
</form> 

この画面は新規エントリー入力確認画面とほぼ同様の内容となるため、新規エントリー入力確認画面のHTMLテンプレートを基にすると作業が簡単になるでしょう。その他の変更は下記のようになります。

/path/to/pieceblog/web/webapp/config/flows/Entry/Edit.yaml
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
...
/path/to/pieceblog/web/webapp/actions/Entry/EditAction.php
<?php
require_once 'Piece/Unity/Service/FlowAction.php';
require_once 'Piece/Unity/Service/FlexyElement.php';

class Entry_EditAction extends Piece_Unity_Service_FlowAction
{
    var $_entry;

    function Entry_EditAction()
    {
        $this->_entry = &new stdClass();
        $this->_entry->title = 'Foo';
        $this->_entry->content = 'Content1
Content2
Content3
';
    }

    function doActivityOnDisplayShow()
    {
        $flexyElement = &new Piece_Unity_Service_FlexyElement();
        $flexyElement->addForm($this->_flow->getView(), $this->_context->getScriptName());

        $viewElement = &$this->_context->getViewElement();
        $viewElement->setElementByRef('entry', $this->_entry);
    }
}

function displayTextArea($value)
{
    return strip_tags(nl2br($value), '<br>');
}

?>

変更が完了したら、ブラウザから画面を確認しましょう。

エントリー参照画面
エントリー参照画面

これで、エントリー参照画面の設計は完了です。なお、この画面には検証すべき入力項目が存在しないため、バリデーション定義は必要ありません。

入力項目の検討とアプリケーションへの反映-エントリー編集画面

エントリー編集画面は、指定されたエントリーの内容の編集や、エントリーの編集確認画面及び削除確認画面への遷移に使われる画面です。この画面の入力項目は、新規エントリー入力画面と同様に「タイトル」「内容」とします。さらに、編集確認画面及び削除確認画面へのリンクをフォームのsubmitボタンに変更します。変更を加えたHTMLテンプレートを下記に示します。

/path/to/pieceblog/web/webapp/templates/Entry/Edit.html
<h4 class="date-header">Edit</h4>
<form name="New" id="New"> 
  <input type="hidden" name="{__flowExecutionTicketKey}" value="{__flowExecutionTicket}" id="flowExecutionTicket" />
  <p class="error" flexy:if="__NewResults.isError(#title#)">{__NewResults.getErrorMessage(#title#)}</p>
  <p>
    Title: <input type="text" name="title" id="title" />
  </p>
  <p class="error" flexy:if="__NewResults.isError(#content#)">{__NewResults.getErrorMessage(#content#)}</p>
  <p>
    Content: <textarea name="content" id="content"></textarea>
  </p>
  <p>
    <input type="submit" name="{__eventNameKey}_ProcessValidateEditFromDisplayEdit" value="Update" />
    <input type="submit" name="{__eventNameKey}_DisplayDeleteConfirmViaDisplayEditFromDisplayEdit" value="Delete" />
  </p>
</form> 

この画面は、新規エントリー入力画面とほぼ同様の内容となるため、新規エントリー入力画面のHTMLテンプレートを基にすると作業が簡単になるでしょう。

次に、新規エントリー入力画面と同様に、バリデーション失敗時のエントリー編集画面への遷移を確認するために、入力値のバリデーション結果によるページフローの分岐を設定しておきます。なお、バリデーション定義は新規エントリー入力画面とまったく同じなので、定義ファイルも同じものを使うことにします。これらを考慮すると、変更は下記のようになります。

/path/to/pieceblog/web/webapp/config/flows/Entry/Edit.yaml
...
  - name: DisplayEdit
    view: Edit
    activity: 
      class: Entry_EditAction
      method: doActivityOnDisplayEdit 
    transition:
      - event: ProcessValidateEditFromDisplayEdit 
        nextState: ProcessValidateEdit 
      - event: DisplayDeleteConfirmViaDisplayEditFromDisplayEdit
        nextState: DisplayDeleteConfirmViaDisplayEdit
...
actionState: 

  - name: ProcessValidateEdit
    activity:
      class: Entry_EditAction
      method: doActivityOnProcessValidateEdit
    transition:
      - event: DisplayEditConfirmFromProcessValidateEdit
        nextState: DisplayEditConfirm
      - event: DisplayEditFromProcessValidateEdit
        nextState: DisplayEdit 
/path/to/pieceblog/web/webapp/actions/Entry/EditAction.php
...
    function doActivityOnDisplayShow()
    {
        $flexyElement = &new Piece_Unity_Service_FlexyElement();
        $flexyElement->addForm($this->_flow->getView(), $this->_context->getScriptName());

        $viewElement = &$this->_context->getViewElement();
        $viewElement->setElementByRef('entry', $this->_entry);
    }

    function doActivityOnDisplayEdit()
    {
        $flexyElement = &new Piece_Unity_Service_FlexyElement();
        $flexyElement->addForm($this->_flow->getView(), $this->_context->getScriptName());
        $flexyElement->restoreValues('New', $this->_entry);
    }

    function doActivityOnProcessValidateEdit()
    {
        $validation = &$this->_context->getValidation();
        if ($validation->validate('New', $this->_entry)) {
            return 'DisplayEditConfirmFromProcessValidateEdit';
        } else {
            return 'DisplayEditFromProcessValidateEdit';
        }
    } 
...

また、バリデーション定義ファイルのレイアウトの階層化のための設定も忘れずに行う必要があります。

/path/to/pieceblog/web/htdocs/edit.php
<?php
error_reporting(E_ALL);

require_once 'Piece/Unity.php';
require_once 'Piece/Unity/Error.php';

Piece_Unity_Error::pushCallback(create_function('$error', 'var_dump($error); return ' . PEAR_ERRORSTACK_DIE . ';'));

$base = dirname(__FILE__) . '/../webapp';

ini_set('session.cookie_path', str_replace('\\', '/', dirname($_SERVER['SCRIPT_NAME'])));
session_save_path("$base/sessions");

$unity = &new Piece_Unity("$base/config", "$base/cache");
$unity->setConfiguration('Dispatcher_Continuation', 'flowName', 'EntryEdit');
$unity->setConfiguration('Renderer_Flexy', 'templateDir', "$base/templates/Entry");
$unity->setConfiguration('Renderer_Flexy', 'compileDir', "$base/compiled-templates/Entry");
$unity->setConfiguration('Configurator_Validation', 'configDirectory', "$base/config/validations/Entry"); 
$unity->setConfiguration('Configurator_Validation', 'cacheDirectory', "$base/cache/validations/Entry"); 
$unity->dispatch();
?>

変更が完了したら、ブラウザから画面を確認しましょう。

エントリー編集画面
エントリー編集画面
エントリー編集画面
エントリー編集画面

表示項目の検討とアプリケーションへの反映-エントリー編集確認画面

エントリー編集確認画面は、エントリー編集画面より入力されたエントリーの内容の確認や、エントリーの更新処理及び編集画面への遷移に使われる画面です。この画面の表示項目は、新規エントリー入力確認画面と同様に「タイトル」「内容」とします。さらに、更新処理及び編集画面へのリンクをフォームのsubmitボタンに変更します。変更を加えたHTMLテンプレートを下記に示します。

/path/to/pieceblog/web/webapp/templates/Entry/EditConfirm.html
<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}_DisplayShowFromDisplayEditConfirm" value="Update" />
  </p>
</form> 

この画面は、新規エントリー入力確認画面とほぼ同様の内容となるため、新規エントリー入力確認画面のHTMLテンプレートを基にすると作業が簡単になるでしょう。その他の変更は下記のようになります。

/path/to/pieceblog/web/webapp/config/flows/Entry/Edit.yaml
...
  - name: DisplayEditConfirm
    view: EditConfirm
    activity: 
      class: Entry_EditAction
      method: doActivityOnDisplayEditConfirm 
    transition:
      - event: DisplayShowFromDisplayEditConfirm
        nextState: DisplayShow
      - event: DisplayEditFromDisplayEditConfirm
        nextState: DisplayEdit
...
/path/to/pieceblog/web/webapp/actions/Entry/EditAction.php
...
    function doActivityOnProcessValidateEdit()
    {
        $validation = &$this->_context->getValidation();
        if ($validation->validate('New', $this->_entry)) {
            return 'DisplayEditConfirmFromProcessValidateEdit';
        } else {
            return 'DisplayEditFromProcessValidateEdit';
        }
    }

    function doActivityOnDisplayEditConfirm() 
    {
        $flexyElement = &new Piece_Unity_Service_FlexyElement();
        $flexyElement->addForm($this->_flow->getView(), $this->_context->getScriptName());

        $viewElement = &$this->_context->getViewElement();
        $viewElement->setElementByRef('entry', $this->_entry);
    } 
...

変更が完了したら、ブラウザから画面を確認しましょう。

エントリー編集確認画面
エントリー編集確認画面

表示項目の検討とアプリケーションへの反映-エントリー削除確認画面

さて、残るはふたつのエントリー削除確認画面のみとなりました。これらは、エントリー参照画面と同様に削除対象のエントリーの内容の確認や、エントリーの削除処理及び参照画面・編集画面への遷移に使われる画面です。この画面の表示項目は、エントリー参照画面と同様に「タイトル」「内容」とします。さらに、削除処理及び参照画面・編集画面へのリンクをフォームのsubmitボタンに変更します。変更を加えたHTMLテンプレートを下記に示します。

/path/to/pieceblog/web/webapp/templates/Entry/DeleteConfirmViaDisplayShow.html
<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}_DisplayDeleteFinishFromDisplayDeleteConfirmViaDisplayShow" value="Delete" />
  </p>
</form> 
/path/to/pieceblog/web/webapp/templates/Entry/DeleteConfirmViaDisplayEdit.html
<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}_DisplayDeleteFinishFromDisplayDeleteConfirmViaDisplayEdit" value="Delete" />
  </p>
</form>

これらの画面は、ページタイトルとsubmitボタンを除いて、エントリー参照画面とまったく同じ内容となるため、エントリー参照画面のHTMLテンプレートを基にすると作業が簡単になるでしょう。その他の変更は下記のようになります。

/path/to/pieceblog/web/webapp/config/flows/Entry/Edit.yaml
...
  - 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
...
/path/to/pieceblog/web/webapp/actions/Entry/EditAction.php
...
    function doActivityOnDisplayEditConfirm()
    {
        $flexyElement = &new Piece_Unity_Service_FlexyElement();
        $flexyElement->addForm($this->_flow->getView(), $this->_context->getScriptName());

        $viewElement = &$this->_context->getViewElement();
        $viewElement->setElementByRef('entry', $this->_entry);
    }

    function doActivityOnDisplayDeleteConfirmViaDisplayShow() 
    {
        $flexyElement = &new Piece_Unity_Service_FlexyElement();
        $flexyElement->addForm($this->_flow->getView(), $this->_context->getScriptName());

        $viewElement = &$this->_context->getViewElement();
        $viewElement->setElementByRef('entry', $this->_entry);
    }

    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);
    } 
...

変更が完了したら、ブラウザから画面を確認しましょう。

エントリー削除確認画面(エントリー参照画面経由)
エントリー削除確認画面(エントリー参照画面経由)
エントリー削除確認画面(エントリー編集画面経由)
エントリー削除確認画面(エントリー編集画面経由)

おわりに

以上で、すべてのフローの画面設計とバリデーション定義は一旦完了となります。この作業によって、アプリケーションのすべての画面の項目が洗い出され、ページフローの見直しが進みました。

次回は、洗い出された項目を基にデータベース設計を行い、アプリケーションにPiece_ORMを使ったデータベースアクセスを組み込みます。

おすすめ記事

記事・ニュース一覧