新規ドキュメントはこちら(http://ozaki.kyoichi.jp/content/blogsection/4/26/)に掲載しております。

フォームの初期化を透過的にする

最近、mojaviがモハビと読まれることが判明しました。これは、mojaviの開発者であるsean kerrさんからの直々の言葉なので、間違いはないです。

さて、今回は今まで手動で組み込んできたHTML_QuickFormを親クラスで処理するようにして、実際にモジュールを作成するときには、ActionやViewでHTML_QuickFormオブジェクトを意識しなくていいようにしたいと思います(コード生成の自動化、実際に記述するコード量の軽減を行います)。具体的にどういった方法で行うかというと、親のActionでHTML_QuickFormを初期化して、リクエスト変数に格納します。また、親のViewでは、リクエスト変数からHTML_QuickFormオブジェクトを取り出し、Smarty変数にセットします。フォームオブジェクトは、別ファイルでクラスファイルとしてAction毎に用意することにします。これを行うためには、命名規則をきめる必要がありますが、今回は、各モジュールにおいて、モジュールディレクトリ配下に、forms というディレクトリを作成して、その下にアクション名Form.class.phpという規則にしたがって、フォームオブジェクトを作成することにします。言葉の説明では分かりづらいと思いますので、下記のサンプルコードを参考にしてみてください

フォームを使うアクションの場合用に、新たにMyFormActionを作成します。MyProject/webapp/lib/MyFormAction.class.phpというファイルで、内容は以下のようになります

<?php

ini_set("display_errors", 0);
ini_set("error_reporting", false);
require_once('/usr/local/share/pear/HTML/QuickForm.php');
ini_restore("error_reporting");
ini_restore("display_errors");

abstract Class MyFormAction extends Action
{

  static
      $form = null;

  public function initialize ($context)
  {
      parent::initialize ($context);

      $module =& $context->getModuleName();
      $action   =& $context->getActionName();

      $formObjectName      = $action.'Form';
      $formObjectDirectory = MO_MODULE_DIR . '/' . $module . '/forms';
      $formObjectFile         = $formObjectDirectory. '/' . $formObjectName . '.class.php';

      if (is_readable($formObjectFile)) {
	          include ($formObjectFile);
	          $form = new $formObjectName;
	          $this->form = $form->execute();
      }	

      $request =& $context->getRequest();
      $request->setAttribute('form', $this->form);

      return true;

  }

}
?>

新規に作成したMyFormActionをautoload.iniに追加します

 MyFormAction       = "%MO_WEBAPP_DIR%/lib/MyFormAction.class.php"

フォームを使うビューの場合用に、新たにMySmaryQuickFormViewを修正します。内容は以下のようになります

<?php
ini_set("display_errors", 0);
ini_set("error_reporting", false);
require_once('/usr/local/share/pear/HTML/QuickForm/Renderer/ArraySmarty.php');
ini_restore("error_reporting");
ini_restore("display_errors");

abstract class MySmartyQuickFormView extends MySmartyView
{

    public function initialize ($context)
    {
        parent::initialize($context);

    	$request =& $context->getRequest();

	if ($request->hasErrors()) {
            $errors =& $request->getErrors();
	    $this->setAttributeByRef('errors', $errors);
	}

	$form =& $request->getAttribute('form');

    	$quickformSmarty = new HTML_QuickForm_Renderer_ArraySmarty($this->getEngine());
    	$form->accept($quickformSmarty);
    
    	$this->setAttribute('form', $quickformSmarty->toArray());

	return true;
    }
}
?>

前回修正した、LoginActionからフォームの初期化部分を抜き出し、MyFormActionで自動的に読み込まれるようにフォームオブジェクトとして作り直します。命名規則にしたがって、MyProject/webapp/module/Default/forms/LoginForm.class.phpというファイルを作成します。内容は以下のようになります

<?php
class LoginForm
{

  function execute()
  {

    $form = new HTML_QuickForm(get_class ($this),
				     'POST',
				     'index.php',
				     '_self', null);

    $form->addElement('hidden', 'module', 'Default');
    $form->addElement('hidden', 'action', 'LoginCommit');
    $form->addElement('text', 'loginName');
    $form->addElement('password', 'password');
    $form->addElement('submit','submit', 'ログイン');

    return $form;
  }

}
?>

ここで重要なのは、クラス名をアクションFormとすること。それとファイル名はアクションForm.class.phpとしてモジュールディレクトリのformsディレクトリの下に作成すること。最後に、必ずHTML_QuickFormオブジェクトを返すexecute()メソッドを実装すること。次に、LoginActionを修正します。前回 validate で実装して、_buildform() を呼び出していた部分を削除します。新しいLoginActionは以下のようになります

<?php
class LoginAction extends MyFormAction
{

    public function execute ()
    {

        return View::INPUT;

    }

    public function handleError ()
    {

        return View::INPUT;

    }
} 
?>

かなりスッキリしたでしょ?注意する点は、MyActionではなく、MyFormActionを継承するように変更したところです。Viewも修正します。Viewの親クラスは新規に作成していないので、継承するクラスは変更しません。その代わり、親クラスで実装するようにしたフォームオブジェクトの取り出しや、Smartyへの変数セットなどの処理は除きます。修正後は下記のようになります(MyProject/webapp/modules/Default/views/LoginInputView.class.php)

<?php
class LoginInputView extends MySmartyQuickFormView
{
    public function execute ()
    {

    	$this->setTemplate('LoginInput.tpl');

    }

}
?>

これでViewもかなりスッキリしたでしょ?ほとんど何も意識しなくてよい。冗長する処理はまとめる事ができました。最後に、ログイン処理が通ったときに表示されるHelloWorldモジュールのHelloWorldDBアクションのビューを修正します。MySmartyQuickFormViewはMyFormActionの初期化処理が必須になるので、このビューは継承できません。ここは、とりあえずMySmartyViewを継承するように修正します

class HelloWorldDBSuccessView extends MySmartyView

{

  ....

最後に、Loginのアクションにアクセスして確認を行います。今までと同じように表示され、動作すれば問題ありません

http://192.168.0.15/MyProject/index.php?module=Default&action=Login

まとめると、今回のポイントは: