ZendFramework入門・その3 フォームを取り扱う・その12007年09月09日 04時21分20秒

ちょっと予定を変更

前回の終わりに書いた予告では、「複数のアクションコントローラを使用する例」をやるつもりだったのですが、よく考えてみたらこれって特に技術的に注意点があるわけじゃないので今回は見送ることにしました。

その代わり、でもないんですが、今回はフォームを触ってみることにします。

フォルダツリーの構築

今回はまた新しいアプリケーションということにします。名前は「zf2」にしておきましょう。

お決まりのように、まずは以下のフォルダ構成を作ってください。

  • htdocs/
    • zf2/
      • application/
        • controllers/
          • IndexController.php
        • views/
          • scripts/
            • index/
              • index.phtml
              • dump.phtml
      • index.php
      • .htaccess
index.php は前回のものをそのまま流用してください。

IIS + ISAPI-Rewriteの環境の方は、httpd.iniにzf2向けの設定を追加することを忘れないでください。以下のようになります。

RewriteRule ^/zf2/[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css)$)[\w\%]*$)? /zf2/index.php [I]

まずは、以下のようなきわめてシンプルな形で作成してみます。

  • 入力フォームを表示するアクション(→ indexAction)
  • indexActionの送信ターゲットで、入力フォームの内容をダンプするアクション(→ dumpAction
ということで、application以下には「IndexController.php」と「index.phtml」「dump.phtml」を作成することになります。

IndexController - 入力フォームの作成

さっそくIndexController.phpを作成してみましょう。まずはindexActionのみの実装です。

<?php
require_once 'Zend/Controller/Action.php';

class IndexController extends Zend_Controller_Action {
	// 初期化処理
	public function init() {
		// BASE要素向けのベースURL
		$this->view->assign( 'baseUrl', getApplicationUrl( $this->getRequest() ) );
	}
	
	// indexアクション
	public function indexAction() {
	}
}
IndexController.php
...はい、indexActionでは特にすることがありません(^^;

対応するindex.phtmlです。これまたなんのひねりもありませんね。

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
    <base href="<?php echo $this->baseUrl; ?>/"></base>
    <title>フォーム テスト</title>
  </head>
  <body>
    <h3>入力フォーム</h3>
    <form action="index/dump" method="post">
      <ul>
        <li>
          <label for="name">名前</label>
          <input type="text" name="name" id="name" size="20">
        
        <li>
          <label for="sex">性別</label>
          <select name="sex" id="sex">
            <option value="1">男性
            <option value="2">女性
          </select>
      </ul>
      <input type="submit" value="送信">
    </form>
  </body>
</html>
index.phtml
なんてことはない、dumpActionに向けてnameとsexというフィールドをpostするだけです。

フォームの入力内容を受け取る

あたりまえなのですが、indexActionから送信されたを受け取る必要があります。$_GETや$_POSTなどのスーパーグローバルを使ってはいけないということはないのですが、Zend Frameworkで用意されている機能を使うようにしましょう。

勘の良い方は、前回index.phpに追加した「getApplicationUrl」のコードからピンとくるかも知れませんが、Zend Framework(というよりZend_Controllerか)でリクエスト情報にアクセスするにはZend_Controller_Requestを使用します。

Zend_Controller_Requestの細かい説明は後回しにして、とりあえず入力内容を受け取って表示するだけのdumbActionを実装してみましょう。以下のような感じです。

<?php
require_once 'Zend/Controller/Action.php';

class IndexController extends Zend_Controller_Action {
	// 初期化処理
	public function init() {
		// BASE要素向けのベースURL
		$this->view->assign(
			'baseUrl',
			getApplicationUrl( $this->getRequest() )
		);
	}
	
	// indexアクション
	public function indexAction() {
	}
	
	// dumpアクション
	public function dumpAction() {
		$this->view->assign(
			'postData',
			$this->getRequest()->getParams()
		);
	}
}
「dumpAction」を追加したIndexController.php

そして、対応するdump.phtmlは以下のような感じです。

<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
    <meta http-equiv="Content-Style-Type" content="text/css">
    <base href="<?php echo $this->baseUrl; ?>/"></base>
    <title>フォーム テスト - 送信内容</title>
    <style>
    .dump {
      border: solid 2px silver;
      overflow: auto;
      height: 250px;
    }
    </style>
  </head>
  <body>
    <h3>送信内容</h3>
    <pre class="dump"><?php var_dump( $this->postData ); ?></pre>
    <hr>
    <a href="index/index">戻る</a>
  </body>
</html>
dump.phtml

実行結果と解説

ここまでのコードを実行してみましょう。まずは「http://<host名>/zf2/」にアクセスすると、以下のようなフォームが表示されます。

入力フォーム

index/index
名前に「dara-j」、性別を「男性」にして送信すると、index/dumpで以下のように表示されます。

送信内容

array(5) {
  ["controller"]=>
  string(5) "index"
  ["action"]=>
  string(4) "dump"
  ["module"]=>
  string(7) "default"
  ["name"]=>
  string(6) "dara-j"
  ["sex"]=>
  string(1) "1"
}

戻る
index/dump

さて、dumpActionとdump.phtmlの解説です。 まず、dumpAction内の「$this->getRequest()->getParams()」ですが、「$this->getRequest()」でリクエストオブジェクトであるZend_Controller_Request_Httpのインスタンスを取得します。getApplicationUrlに渡している部分と同じですね。 そして、取得したリクエストオブジェクトの「getParams()」メソッドを実行した結果を'postData'としてviewにassignしています。

Zend_Controller_RequestのgetParamsメソッドは、getパラメータやpostパラメータなどの、クライアントから要求されたパラメータすべてを格納した連想配列を取得するメソッドです。(※ 実際にはsetParam/setParamsによって、コントローラ処理中に任意に設定されたパラメータも含まれますが、ここではそういった使い方には触れていません)

そして、dump.phtmlでは、'postData'としてassignされた連想配列をダンプしています。

getParams()の中身とその他のアクセスメソッド

出力された連想配列をみて「あれ?」と思いませんでしたか?キー「controller」が「index」、「action」が「dump」...そうです。どのコントローラのどのアクションが指定されたか、の情報もリクエストオブジェクトに含まれるのです。

ここではgetParams()でいっさいがっさいを取得しましたが、リクエストオブジェクトに対してgetControllerName()やgetActionName()といったメソッドでこれらの情報を取得することもできます。

リクエストオブジェクトZend_Controller_Request_Httpの主なアクセスメソッドには以下のようなものがあります。

  • mixed getParams() → ユーザパラメータやGETパラメータ、POSTパラメータを格納した連想配列を取得する
  • mixed getParam(string $key, [mixed $default=null]) → キー$keyに関連付けられたパラメータ値を取得する。$defaultを指定した場合、$keyが指定されていない場合のデフォルト値にできる
  • mixed getPost([string $key=null], [mixed $default=null]) → POSTパラメータを取得する。$keyを指定した場合は$keyに対応する値のみ、省略した場合は$_POSTそのものが返る。$defaultはgetParam()と同じ。
  • mixed getQuery([string $key=null], [mixed $default=null]) → getPost()のGET版
その他のメソッドやメンバはAPIリファレンスで調べてみてください。

今回のまとめ

今回のポイントは以下のとおりです。

  • フォームの入力値を取得するには、Zend_Controller_Request_Httpを使用して、getParams()メソッドを使用する
  • getParams()で取得できる連想配列にはコントローラやアクションの情報も含まれる
  • Zend_Controller_Request_Httpには、他にもgetParam()、getPost()などの情報へのアクセスメソッドがある
まとめてみると、また薄い内容でしたね...

今回の反省&次回予告

本当はこのあとに「受け取った入力値をもう一度フォームにまわす」ようなサンプルを載せるつもりだったのですが、思ったより記事が長くなったので今回はここまでにしました。単純に受け取った値をフォームに当てはめて表示させるだけなら今回の記事から簡単に作れるでしょうから、皆さん試してみてください。

さて、次回は今回のフォーム入力をベースに、他のアクションへ処理を委譲する方法とマジックメソッド、関連して(ようやく)基本的な例外処理方法について書こうと考えていますのでお楽しみに。

ZendFramework入門・その2 アクションメソッドの追加とリンクの扱い方2007年09月03日 02時08分59秒

のっけからお詫び

すみません、このエントリ書くまで気づきませんでしたが、前回示したフォルダツリーで、ビュースクリプト関連で1階層抜けてました。具体的には「htdocs/zf1/application/views/scripts/index/」の「scripts」部分が抜けてました。どうもすみませんでした。

2つ目のアクションメソッド

気を取り直して、本題です。

少し期間が空いてしまいましたが、前回の続きでアクションコントローラを扱います。前回はデフォルトのアクションメソッド(indexAction)のみの実装でしたが、まずは2つ目のアクションメソッドを実装するところからはじめましょう。

アプリケーションは前回のものをそのまま流用しますので、これからやる作業は

  • IndexController.phpにアクションメソッド「secondAction」を追加する
  • secondActionに対応するビュースクリプト「second.phtml」を/views/scriptsに作成する
の2点です。

/application/controllers/IndexController.phpを以下のようにします。

<?php
require_once 'Zend/Controller/Action.php';

// IndexController
class IndexController extends Zend_Controller_Action {
	// indexアクション
	public function indexAction() {
		$this->view->assign( 'message', 'ようこそ Zend Frameworkへ' );
	}
	
	// 今回追加したsecondアクション
	public function secondAction() {
		// indexActionと同様にメッセージをassignする
		$this->view->assign( 'message', '2つ目のアクション' );
	}
}
IndexController.php

対応するsecond.phtmlは、とりあえずindex.phtmlをそのままコピーしてリネームしてください。

ここまでの作業で、ファイル一式は以下のようになります。

  • htdocs/
    • zf1/
      • application/
        • controllers/
          • IndexController.php → アクションメソッド「secondAction」を追加
        • views/
          • scripts/
            • index/
              • index.phtml
              • second.phtml → secondActionに対応するビュースクリプト
      • index.php
      • .htaccess → Apacheを使用している方はこれを忘れずに
この状態で「http://<host名>/zf1/index/second」にアクセスすると、「2つ目のアクション」と表示されるはずです。

以降、基本的には、「アクションメソッドを増やしたら、対応するビュースクリプトを追加する」というような拡張方法になります。

相互にリンクを張ってみる

さて、せっかくアクセスできるURLが2つに増えたのに、それぞれいちいちURLを直接打ち込んでいたら面倒この上ないので、それぞれのアクションメソッド(のビュー)からお互いにリンクを張ってみることにします。

ビュースクリプト中に直接記述してもいいのですが、リンクアドレスの情報はコントローラ側で管理し、ビュースクリプトはそれをレンダリングするだけに分離するのが筋だと思いますので、そのようにしてみます。

まずは2つのアクションメソッド内で「$this->view」にリンクさせるアドレスをassignさせます。こんな感じでしょうか。

<?php
require_once 'Zend/Controller/Action.php';

// IndexController
class IndexController extends Zend_Controller_Action {
	// indexアクション
	public function indexAction() {
		$this->view->assign( 'message', 'ようこそ Zend Frameworkへ' );
		
		// secondActionへのリンクをビューへ追加
		$this->view->assign( 'link', 'index/second' );
	}
	
	// secondアクション
	public function secondAction() {
		$this->view->assign( 'message', '2つ目のアクション' );
		
		// indexActionへのリンクをビューへ追加
		$this->view->assign( 'link', 'index/index' );
	}
}

そしてビュースクリプトです。index.phtml、second.phtmlとも以下の内容に修正します。

<html>
<head>
	<title>zf1</title>
</head>
<body>
	<h3>
		<?php echo $this->message; ?>
	</h3>
	<hr>
	
	<!-- プロパティ「link」の内容をhrefに使用する -->
	<a href="<?php echo $this->link; ?>"><?php echo $this->link; ?></a>
</body>
</html>
index.phtml、second.phtml

さて、まずは「http://<host名>/zf1/」にアクセスしてみてください。以下のようになるはずです。

ようこそ Zend Frameworkへ


index/second
http://<host名>/zf1/
ここまでは従来と変化がほとんどないので問題ないはずです。確認点としてはリンクがちゃんと表示されているか、くらいでしょうか。

次にページ中のリンク「index/second」をクリックしてみてください。以下のようになります。

2つ目のアクション


index/index
http://<host名>/zf1/index/second/
ちゃんと表示が変わっているでしょうか?

リンク先がなんだかヘン

さて、ここまで特に問題はないはずなのですが、どこかおかしい所はないでしょうか?

index/secondに表示されているリンクをよく確認してください。未訪問の色のままになっていませんか

リンク「index/index」にカーソルを合わせてみるとわかると思いますが、実は現在のサンプルのままでは、リンク先が「http://<host名>/zf1/index/index/index」になってしまいます。

よくよく考えてみると(よくよく、でなくてもわかりますが)、Zend Frameworkのコントローラモデルでは、すべてのアクセスがディレクトリパスに展開されるため、相対でリンクを示すと常に現在のパスに対して階層が追加されてゆくことになってしまいます。

これが、アプリケーションがホストのトップであれば、「/index/second」などと絶対パスが使用できるので問題ありませんが、アプリケーションごとにホストを用意したりバーチャルホストを用意したりするのはあまり現実的ではありません。

また、ドキュメント中に出現するURLはリンク先だけではありません。JavaScriptやCSSなどのリソース、画像、さらにはformのpost先など、1つのWebアプリケーション内でアプリケーション内のほかのURLを指定する必要があるところはたくさんありますので、このままではちょっと困ったことになってしまいます。

さて、どうやって解決したものでしょうか...

base要素を活用

これはdara-j的なアプローチですが、リンクの解決にbase要素を使用してみましょう。そう、すべてのページのhead内で「<base href="http://<host名>/zf1/"></base>」が出力されてさえいれば、前述のように「コントローラ名/アクション名」形式でリンクを指定することができます。

base要素に指定するアプリケーションのルートURLですが、直接ビュースクリプト中に指定してもよいのですが、他のアプリケーションを構築する際にもそのまま流用できるように関数を作成することにします。

起動スクリプト「index.php」に以下のような関数を追加します。

function getApplicationUrl($request) {
	if( $request == null ) return false;
	
	return ( preg_match('/^HTTPS/i', $request->getServer('SERVER_PROTOCOL')) ? 'https' : 'http' )
		. '://'
		. $request->getServer('HTTP_HOST')
		. $request->getBaseUrl();
}
引数として受け取る「$request」は、Zend_Controller_Request_Httpというオブジェクトになります。このオブジェクト(クラス)は従来$_GETや$_POSTなどのスーパーグローバル変数から取得していたリクエスト情報を一元的に集約したバリューオブジェクトです。当然Zend Framework上でも従来のスーパーグローバル変数へアクセスはできますが、せっかくのオブジェクト指向プログラミングでもあり、なるべくこちらを使うようにしたほうがよいと思います。

アクションコントローラの初期化機構

では、このgeApplicationUrl関数を使用して、実際にbase要素を出力するようにしてみましょう。引数にZend_Controller_Request_Httpが必要ということもあり、

  • アクションコントローラからgetApplicationUrlを呼び出して値を取得し、
  • ビュースクリプトへassignする
というアプローチにしてみたいと思います。

これまでと同じように、各アクションメソッド内から$this->view->assignとしてもよいのですが、これはコントローラ内での共通処理とするような内容なので、どこか一箇所に集約しましょう。

アクションコントローラは固有の初期化処理用として、「init」メソッドを用意しています。これはZend_Controller_Actionのコンストラクタから始まる一連の初期化処理の中でサブクラス固有の初期化処理用として用意しているもので、これをオーバーライドすることになります。

initメソッドをオーバーライドし、base要素向けのベースURLをビューへassignするコードを追加したIndexController.phpは以下のようになります。

<?php
require_once 'Zend/Controller/Action.php';

// IndexController
class IndexController extends Zend_Controller_Action {
	// initメソッド。初期化処理用のフックメソッド
	// アクセスレベルが「public」な点に注意
	public function init() {
		// リクエストオブジェクト(Zend_Controller_Request_Abstract)を取得
		$request = $this->getRequest();
		
		// ベースURLを取得
		$baseUrl = getApplicationUrl( $request );
		
		// ビューへ割り当てる
		$this->view->assign( 'baseUrl', $baseUrl );
	}
	
	// indexアクション
	public function indexAction() {
		$this->view->assign( 'message', 'ようこそ Zend Frameworkへ' );
		
		// secondActionへのリンクをビューへ追加
		$this->view->assign( 'link', 'index/second' );
	}
	
	// secondアクション
	public function secondAction() {
		$this->view->assign( 'message', '2つ目のアクション' );
		
		// indexActionへのリンクをビューへ追加
		$this->view->assign( 'link', 'index/index' );
		
		// indexAction用のビュースクリプトをレンダリング
		$this->_helper->viewRenderer( 'index' );
	}
}
IndexController.php(初期化処理を追加)
ちょっと補足になりますが、getApplicationUrl()に渡すリクエストオブジェクトを「Zend_Controller_Request_Http」と説明していましたが、コード中のコメントでは「Zend_Controller_Request_Abstract」としています。 これは別に先の説明で嘘をついていたとか間違っているとかではなく、Zend_Controller_Action::getRequest()メソッドは抽象クラス「Zend_Controller_Request_Abstract」を返す仕様になっていて、httpアクセス環境化ではhttp通信向けサブクラスである「Zend_Controller_Request_Http」を返している、ということです。getApplicationUrl()内ではZend_Controller_Request_Httpであることを前提としているのであまりよいコードではないのですが、この連載中ではCLIなどは想定していないのでこのままでご容赦ください。

もう一点補足です。secondActionメソッドの最後に「$this->_helper->viewRenderer('index')」という見慣れないコードを追加していますが、これはまったく同じ内容の2つのビュースクリプトを定義しているのが気持ち悪いため、2つのアクションメソッドで1つのビュースクリプトを共用するためのコードです。

_helperやviewRendererがいったい何なのかはこの連載でもいずれ取り上げますが、「viewRendererヘルパーの(暗黙のメソッドである)setRenderメソッドでレンダリングするビュースクリプトを変更できる」ということができる、ということだけ理解してくだされば問題ないと思います。詳細を知りたい場合はリファレンスガイドの「7.8. アクションヘルパー」、および同ページ中の「例 7.8. 別のビュースクリプトの選択」をご覧ください。

他のソースの改変

init内で「baseUrl」として割り当てられたURLをbase要素として出力するよう改変したindex.phtmlは以下のようになります(second.phtmlは不要になったので削除してください)。

<html>
<head>
	<title>zf1</title>
	<!-- base要素の出力。末尾の「/」に注意 -->
	<base href="<?php echo $this->baseUrl; ?>/"></base>
</head>
<body>
	<h3>
		<?php echo $this->message; ?>
	</h3>
	<hr>
	
	<!-- プロパティ「link」の内容をhrefに使用する -->
	<a href="<?php echo $this->link; ?>"><?php echo $this->link; ?></a>
</body>
</html>
index.phtml(base要素の出力を追加)
base要素の出力時で、href属性の最後に「/」を記述しているのは、単にgetApplicationUrl()が末尾に「/」がない状態の文字列を返すからです。base属性は末尾が「/」ではない場合有効に機能しないので注意してください。

そしてgetApplicationUrl関数を追加した、最終的なindex.phpは以下のようになります。

<?php
// エラーを表示するようにしておく
ini_set( 'display_errors', 1 );

// フロントコントローラをrequire
require_once 'Zend/Controller/Front.php';

// ルーティング&ディスパッチ開始
Zend_Controller_Front::run( './application/controllers' );

// アプリケーションのベースURLをリクエスト情報から生成
function getApplicationUrl($request) {
	if( $request == null ) return false;
	
	return ( preg_match('/^HTTPS/i', $request->getServer('SERVER_PROTOCOL')) ? 'https' : 'http' )
		. '://'
		. $request->getServer('HTTP_HOST')
		. $request->getBaseUrl();
}

index.php(getApplicationUrl関数追加)

今回の最終的なファイルツリーは以下のようになります。

  • htdocs/
    • zf1/
      • application/
        • controllers/
          • IndexController.php → init()、secondAction()を追加
        • views/
          • scripts/
            • index/
              • index.phtml → ベースURLとリンクの出力を追加
      • index.php → getApplicationUrl()関数を追加
      • .htaccess

今回のまとめ

今回のポイントは以下のようになります。

  • アクションメソッドを増やしたら、対応するビュースクリプトを追加する
  • 相対URLを正常に使用するにはbase要素でアプリケーションのベースURLを指定する
  • リクエスト情報の取得には、スーパーグローバルの代わりにZend_Controller_Request_Httpを使用する
  • アクションコントローラの初期化はinit()メソッドをオーバーライドする
  • ビュースクリプトの重複・分散を避けるために、$this->_helper->viewRenderer()を使用して他のアクションメソッドに関連付けられたビュースクリプトを共用することもできる

次回の予定

次回は(今回やる予定だった)エラーの処理方法の概要と、関連して複数のアクションコントローラを使用する例を扱ってみたいと思います。

ZendFramework入門・その1 メッセージを表示するアプリケーションを作ってみる2007年08月29日 03時28分49秒

はじめの一歩 - メッセージを表示してみる

前回の準備編でZend Frameworkの環境は整ったので、ものすごく簡単なアプリケーションから作ってみます。アプリケーションルートを「zf1」とします。

ディレクトリ構成

まずはディレクトリ構成。ドキュメントルート直下にフォルダ「zf1」を作成し、その下に「application」を作成します。さらに「application」下に「controllers」「views」を作成、「views」下に「index」を作成してください(結構面倒くさいでしょ?)。フォルダの名前に注意してください。特に「views」は、暗黙で参照される名前ですので、末尾の「s」が抜けていたりなどスペルミスがあるといきなり「ファイルが見つからない」エラーになってしまいます。

※:2014-04-08 説明文中の「applications」を「application」に修正しました。コメントでのご指摘、ありがとうございました。

ドキュメントルートからのツリーは以下のようになります。

  • htdocs/
    • zf1/
      • application/
        • controllers/
        • views/
          • scripts/
            • index/

index.phpを作成する

まずはURLリライトで必ず呼び出されるようにする「index.php」を作成します。このスクリプトがすべてのリクエストを処理することになります。

zf1の下に以下の内容でindex.phpを作成してください。

<?php
// エラーを表示するようにしておく
ini_set( 'display_errors', 1 );

// フロントコントローラをrequire
require_once 'Zend/Controller/Front.php';

// ルーティング&ディスパッチ開始
Zend_Controller_Front::run( './application/controllers' );

index.php
ここで行っているのは、以下の3つの処理です。
  • ini_set()でエラーをブラウザへ表示するように設定(デバッグ向け)
  • フロントコントローラクラスのソースをrequire
  • ルーティング&ディスパッチ処理の開始

フロントコントローラ?

いきなり、あたりまえのように「フロントコントローラ」と言われてもなんのことかわからないかも知れませんので、簡単に説明します。

Zend Frameworkのリクエスト処理は、「フロントコントローラがリクエストを受け付けて、ルータに行き先(ディスパッチ先)を決めさせ、決定した行き先(アクションコントローラ)へディスパッチャを介して処理を委譲する」という流れになります。さらに用語が増えていますが^^;、順次解説していきますので、とりあえずは気にせず「こんな用語があるんだ」程度に考えていてください。

この「フロントコントローラ」が、コード中に記述されている「Zend_Controller_Front」で、このクラスは「Zend/Controller/Front.php」で定義されています。

命名規則

こうしてクラス名とファイル名を並べてみるとちょっとしたことに気づくかもしれません。Zend Frameworkのクラスとそれを定義するファイル名は単純なルールで決められています。

フロントコントローラ「Zend_Controller_Front」のアンダースコアをパス区切りに置き換え、末尾に「.php」を補うと、インクルードパスを基点にしたファイルパスになります。当然ファイルパスのパスセパレータをアンダースコアに置き換えて「.php」を取り除くとクラス名になります。

もう少し例を見てみましょう。ここではまだ使用しませんが、データベースのテーブルを取り扱うための「Zend_Db_Table」という抽象クラスがありますが、これは「Zend/Db/Table.php」で定義されています。さらにこれの派生元になっている「Zend_Db_Table_Abstract」抽象クラスは「Zend/Db/Table/Abstract.php」で定義されています。

これらのルールを把握していれば、あるクラスを使用するためにrequireしなければならないファイルパスがすぐわかると思います。このルールを適用するために、準備編でインクルードパスの設定をしていた、というわけです。

アクションコントローラを作成する

さて、いささか話がそれましたが、続けてコードを作成していきます。上記のindex.phpでは委譲先を決定して呼び出すところまでは行ってくれますが、肝心の委譲先がない状態ですので当然ながらエラーになります。ですので、次のステップでは処理の委譲先であるアクションコントローラを作成します。

アクションコントローラは、フロントコントローラから開始されたディスパッチ処理の流れの中で、実際に要求を処理するための「アクション」を担当します。

アクションコントローラの命名規則

アクションコントローラにも命名規則があり、

  • クラス名はCamelCase形式で「コントローラ名」とサフィックス「Controller」で構成する必要がある。
  • アクションメソッド(実際にディスパッチ処理で呼び出されるメソッド)はcamelCase形式で、「アクション名」とサフィックス「Action」で構成する必要がある。
  • ファイル名はクラス名+.phpである必要がある。
というルールに従う必要があります。

コントローラとアクションの決定

またちょっと横道にそれますが、「コントローラ名」と「アクション名」という用語がでてきましたが、それがどのように決定されるかわからないかもしれません。

これらの情報はリクエストに含まれることになります。今回のアプリケーション「zf1」に対するリクエストURLが「http://<host名>/zf1/index/test」だったとすると、アプリケーションのルートになるURLは「http://<host名>/zf1/」で、そのあとの「index」がコントローラ名、最後の「test」がアクション名になります(さらにこの後ろに「<パラメータ名>/<パラメータ値>」という形式でGETのパラメータを渡すようになります)。

このリクエストを処理するのが「IndexController」(コントローラ名の先頭を大文字にします)に実装された「testAction」メソッド、ということになります。

また、リクエストでアクション名が省略された場合は「index」アクション、コントローラ名が省略された場合は「index」コントローラが指定されたものとみなされます。つまり、「http://<host名>/zf1/」(コントローラ・アクションとも省略)も「http://<host名>/zf1/index/」(アクションのみ省略)も「http://<host名>/zf1/index/index/」が指定されたことになります。

アクションコントローラの実装

これらの知識を元に、最低限のアクションコントローラを作成してみましょう。デフォルトの「IndexController」です。

<?php
require_once 'Zend/Controller/Action.php';

// IndexController
class IndexController extends Zend_Controller_Action {
	// indexアクション
	public function indexAction() {
		// アクションに必要なコードをここに記述します。
	}
}
IndexController.php
本当に「とりあえず」といったコードですが、これをzf1下の application/controllers/IndexController.phpとして保存してください。

ビュースクリプトの作成

ここまでの長々とした説明でようやく「index.php」、「application/controllers/IndexController.php」の2つのファイルを作成しましたが、これだけではまだ動作させられません。「あぁ、もううんざり」と思うかもしれませんが、あと少しだけお付き合いください(^^;。

ビュースクリプトとは

実はここまでではまだ「MVC」の「C」の部分しか取り扱っていません。「M」(モデル)の部分はデータベース周りのことになるのでまた後ほどにしておくとして、あとは「V」、つまりビューを取り扱う必要があります。

Zend Frameworkでビューを扱うのは、そのまんまの名前の「Zend_View」になります。

実は、アクションコントローラはデフォルトでZend_Viewのインスタンスを与えられており、パブリックプロパティ「view」を介して値を「assign」することによりZend_Viewに出力させたいデータを渡すことができます。

そしてアクションコントローラがなにも支持をしなくても、Zend_Viewはディスパッチ処理が完了すると命名規則に従ったビュースクリプトを自動的に実行して、コントローラからassignされたデータをレンダリングするようになっています。

ビュースクリプトの命名規則

ここで、最初にあげたディレクトリ構成を見直してみてください。zf1/applicationの下に「views/scripts」というディレクトリを用意しています。これが先にあげた「命名規則」に従ったパスの基点で、この下にさらにコントローラ名に一致する名前のディレクトリを作成し、その中にアクション名に一致するビュースクリプトファイルを設置することで、ようやくリクエストを処理してブラウザへ表示することができるようになります。(面倒くさいですか?dara-jは面倒くさいと思います。全力で。)

ビュースクリプトの実態

ビュースクリプトファイルは、HTMLファイルにPHPコードを埋め込んだ、ある意味典型的なPHPファイルです。ただし、いくつかの注意点があります。

  • ファイルの拡張子は「.phtml」にする必要がある。
  • 実行時はアクションコントローラに関連付けられたZend_Viewのコンテキストで動作する。
1点目は実際にはカスタマイズ可能なのですが、フロントコントローラから開始される一連の流れではデフォルトの設定をそのまま使用したほうがよいので、この入門記事中ではビュースクリプトの拡張子は常に.phtmlにします。

2点目はどういうことかというと、ビュースクリプト中で「$this」を参照するとそれがZend_Viewのインスタンスである、ということです。具体的には以下のコードで説明しますが、アクションメソッド内から「$this->view->assign('hoge', 'hoge');」とすると、そのアクションに対応するビュースクリプト内からは「$this->hoge」にアクセスでき、その値が'hoge'になる、といった使い方をします。

ようやく実装

さて、ようやくコードです。まずは先ほどのIndexController.phpを修正します。indexActionメソッドを以下のようにしてみましょう。

<?php
require_once 'Zend/Controller/Action.php';

// IndexController
class IndexController extends Zend_Controller_Action {
	// indexアクション
	public function indexAction() {
		// 変更箇所。Zend_Viewの「message」に「ようこそ Zend Frameworkへ」を割り当てる
		$this->view->assign( 'message', 'ようこそ Zend Frameworkへ' );
	}
}
IndexController.php(改訂版)
そしてビュースクリプトです。今回はIndexControllerのindexActionなので、application/views/scripts/index/index.phtml を作成します。HTMLとしての記述は思い切り端折っていますので、気になるようでしたら補完してください。
<html>
<head>
	<title>zf1/index/index</title>
</head>
<body>
	<h3>
		<?php echo $this->message;  ?>
	</h3>
</body>
</html>
index.phtml

これでようやく今回作成するファイルが出揃いました。最終的にツリー構成は以下のようになります。

  • htdocs/
    • zf1/
      • application/
        • controllers/
          • IndexController.php
        • views/
          • scripts/
            • index/
              • index.phtml
      • index.php

zf1へブラウザでアクセスして、「ようこそ Zend Frameworkへ」が表示されればOKです。

次回の予定

ようやく最小限の構成ができたわけですが、単独のアクションを実装しただけではまったく意味がありません。次回は複数のアクションメソッドの実装を中心に、エラーを処理するコントローラについても触れたいと考えています。

参考資料

今回触れたZend_Controller周りの詳細を知りたいのでしたら、ぜひリファレンスガイドに目を通してください。

またもこっそり訂正 (07.08.29 11:35)

PHPの開始タグの「?」を忘れたり、開始タグそのものを忘れたりしてたので訂正しました。

さらにこっそり訂正(07.08.31 00.53)

h3タグの閉じミスとビュースクリプト中のPHP閉じタグの記述ミスを修正しました。あかんなぁ...

さらにさらにこっそり訂正(07.09.03 02:24)

フォルダツリーでビュースクリプトのツリー構成から1階層抜けていたため、追加しました。正しくは「views/scripts/index/」になります。

ZendFramework入門・準備編 Zend Frameworkの動作環境を作る2007年08月28日 02時53分30秒

突然入門記事を書いてみたり

えー、なにを思ったか、Zend Frameworkの入門記事を書いてみたいと思います。とりあえずの第一段階として、Zend Framework標準のMVCアーキテクチャを使用して、フォームの内容をDBに書き込んであとで検索できるような簡単なメモアプリを作ることを目標にしようと思います。

dara-jにそんなスキルがないので、PHPやオブジェクト指向プログラミングの詳しい解説は省略させていただきます。前提として

  • PHPをある程度使える(他人のソースをほんのちょっと改造できる程度)
  • クラスベースのオブジェクト指向プログラミングをかじったことがある
くらいのスキルがあるものとさせていただきます。(とはいっても、掲載コードにはなるべく解説をいれるつもりですが)

必要な環境

Zend Frameworkでアプリケーションを作成・動作させるには以下のような環境が必要になります。

  • PHP(5.1.4以降): Zend FrameworkはPHP5のオブジェクト指向機能を利用しているため5.1.4またはそれ以降のPHP環境が必要になります。
  • URLリライト機能があるWebサーバ:Apacheであればmod_rewriteを有効にすれば問題ありません、IISでもISAPI Rewriteを使用すればZend Frameworkを動作させることができます。
  • テキストエディタ:ソースコードの編集に必要です。本当はPDTのようにコード補完機能が利用できるIDEがあればいいのですが、ここで取り扱う規模なら手になじんだテキストエディタで十分でしょう。
  • Web ブラウザ:Webアプリケーションを作るので、当然ながら動作確認にブラウザが必要です。

インストール

まずはPHPとWebサーバをインストールする必要がありますが、ここでは省略します。検索すれば有用な記事がたくさん見つかると思うのでそれらを参考にして、PHPが動作する環境を作ってください。

PHPが正常に動作する環境が確保できたら、Zend Frameworkをダウンロードします。こちらのダウンロードページからダウンロードしてください。

※:この記事を書いている2007年 8月 28日時点では、1.0.1が最新バージョンです。この記事は1.0.0をベースに進めますがバージョン間の相違はおそらくないのでどちらでも問題ないと思います。

1.0.0(または1.0.1)のアーカイブをダウンロード・解凍すると、以下のようなディレクトリ構成になっているはずです。

  • ZendFramework-1.0.x/ → ルートフォルダ
    • demos/ → デモアプリケーションフォルダ。この連載では使用しません
    • incubator/ → 正式のライブラリになる前の実験的(?)なライブラリ郡があります。この連載では使用しません
    • library/ → ライブラリのルートフォルダ
    • tests/ → テストスクリプトフォルダ。この連載では使用しません
ルートフォルダ(ZendFramework-1.0.x)、または/libraryフォルダを任意の場所に設置してください。ただし、後述しますがlibraryフォルダをPHPのインクルードパスに追加する必要があるため、ドライブのルートに設置したほうが楽でしょう。たとえばD:\ZendFramework-1.0.0、のように。

URLリライトの動作確認

ZendFrameworkの設定と動作確認の前に、URLリライトの設定と動作確認をとってみましょう。Apacheを使用しているのであれば、書き換え対象のフォルダに以下のようにルールを記述した.htaccessを設置するだけですみます。

RewriteEngine on

RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php
これは、「.js、.ico、.gif、.jpg、.png、.css以外の名前のパスをすべてindex.phpにリライトする」というルールです。Zend Framework(性格にはZend_Controller_*を使用したMVCモデル)では、このようにすべてのリクエストをアプリケーションルートのindex.phpに処理させるようにします。

今回は、ドキュメントルート下に「rewrite_test」というフォルダを切って、これを確認用のURLのルートにします。ドキュメントルートを「D:\htdocs」にした場合、以下のようなファイル配置になります。(ごめんなさい、Windows前提です。linuxなどは適当にルートやパス区切りを読み替えてください)

  • D:\
    • htdocs/
      • rewrite_test/
        • .htaccess → ここにリライトルールを記述
        • index.php → 画像ファイルとJavaScript、スタイルシート以外へのアクセスはすべてここに集約される
        • test.html → ダミーで設置
index.phpの内容は以下のような簡単なものにしてみましょう。
<?php
echo 'hello, Zend Framework !';

この状態で/rewrite_test/test.html(ダミーファイルへのURL)へアクセスして、「hello, Zend Framework !」と表示されれば、URLリライトの機能は正常です。もしtest.htmlが表示されるようであれば、Apacheの設定でmod_rewriteがONになっているか再度確認してください。

IIS + ISAPI Rewriteの場合

IIS + ISAPI Rewriteの環境の場合はApacheよりもちょっとだけ面倒です。ルールの記述はISAPI Rewriteのインストールディレクトリにある「httpd.ini」に記述する必要があります。さらに書式もちょっと面倒です。上記環境の場合は以下のようになります。

[ISAPI_Rewrite]
RewriteRule ^/rewrite_test/[\w/\%]*(?:\.(?!(?:js|ico|gif|jpg|png|css)$)[\w\%]*$)? /rewrite_test/index.php [I]
これ以降、アプリケーションフォルダを切るたびに対象のパスに対する同様の記述をhttpd.iniへ追加していく必要があります。まぁ、コピーペーストでパスだけ書き換えればいいのですが。

インクルードパスの設定

URLリライト機能の確認ができたら、Zend FrameworkのlibraryフォルダをPHPのインクルードパスに設定します。たとえば、アーカイブを解凍したルートフォルダごと、Dドライブ直下に設置した(つまり、D:\ZendFramework-1.0.0 になっている)場合は、PHP.iniのinclude_pathを以下のように設定します。

; Windows向けの例
include_path ".;D:\ZendFramework-1.0.0\library"
もし、他のライブラリを常用するのであればそちらのパスも忘れずに記述しておいてください。

PHP.iniを書き換えたら、CGIモードでない限りは設定を反映させるためWebサーバを再起動する必要があります。

いよいよZend Frameworkのライブラリを使ってみる

インクルードパスが正しく設定されていれば、Zend Frameworkのライブラリにあるクラス郡を使用できます。先ほどのindex.phpを以下のように変更してみましょう。

<?php
// Zend_Version クラスをrequireする。
require_once 'Zend/Version.php';

// Zend Frameworkのバージョン情報をechoする
echo 'hello, Zend Framework ! version = ' . Zend_Version::VERSION;
/rewrite_test/index.php にアクセスして、「hello, Zend Framework ! version = 1.0.0」と表示されれば成功です。

もしrequire_onceでWarningがあがるようならインクルードパスが適切に設定されていません。PHP.iniをもう一度見直してみてください。

これでZend Frameworkの環境ができあがりました。次回からはもう少しプログラミングっぽくしていこうと思います。

あとがき

dara-jは全然PHPerではないのですが、現在とっかかってる仕事がZend Frameworkを使用しているので、日々PHPやZend Frameworkと格闘しています。

いまではそこそこ慣れたのですが、使い始めのころはZend Frameworkの「プログラマ向けリファレンスガイド」と首っ引きでした。もちろん、このリファレンスガイドとAPIドキュメントがあれば大体のことは網羅されていますし、当然ソースコードも公開されているのでそちらを追いかければより詳細な仕様を確認できるのですが、とっかかりとしては敷居が高く、かなりうなりながらドキュメントを読んでいたように思います。

「PHPフレームワークの本命」と言われている割に、検索をしてみてもあまりまとまった入門記事にあたらないように思います。ITmediaの記事や、IBM developerWorksの記事もありますが、若干古い内容なため、現状の1.0系とはところどころ違っている部分もあるので、ひょっとするとdara-jが学んだようなことがだれかの役に立つかもしれない、と思いこの記事を書き始めました。

コードはなるべく検証してから掲載するように心がけますが、間違いやバグ、環境によって動作しないような部分があったらコメントやトラックバックでお知らせいただければ幸いです。

仕事などの関係もあるのでそうしょっちゅうは記事をかけないかも知れませんが、気長に読んでいただければ幸いです。

こっそり訂正 (07.08.28 11:48)

PHPのソース中の開始タグの「?」が抜けてました。間抜けすぎます。