どーも!marusukeです!
前回の記事で、blogのモデル(LaminasDbSqlCommandクラス)を作りました!今回は、view側を作成していきます!
作成の流れとしては、
- Formクラスを作成する。
- コントローラーを作る。
- コントローラーのインスタンス化のためのFactoryとルートを作る。
- モデル(repositoryクラス)を作成する。
- view側を作る。(コントローラーも少し修正します)<= 現在ここです!
早速view側を作りましょう!
- view側(add.phtml)で実現したいこと
- view側(add.phtml)を作成する!
- // 1 ~ // 16の解説
- // 1「$form = $this->form;」について
- // 2「$form->setAttribute(‘action’, $this->url());」について
- // 3「$fieldset = $form->get(‘post’); 」について
- // 4「$title = $fieldset->get(‘title’);」について
- // 5 $title->setAttribute(‘class’, ‘form-control’);について
- // 6「$title->setAttribute(‘placeholder’, ‘Post title’);」について
- // 7「$submit = $form->get(‘submit’); 」について
- // 8「$submit->setAttribute(‘class’, ‘btn btn-primary’);」について
- // 9「$form->prepare();」について
- // 10「echo $this->form()->openTag($form);」について
- // 11「$this->formLabel($title)」について
- //12「$this->formElement($title)」について
- // 13「$this->formElementErrors()->render($title, [‘class’ => ‘help-block’])」について
- //14「echo $this->formSubmit($submit);」について
- //15「echo $this->formHidden($fieldset->get(‘id’));」について
- //16「echo $this->form()->closeTag();」について
- // 1 ~ // 16の解説
- Formから入力されたデータをDBに保存できるようにコントローラーを修正する!
- blogをブラウザに表示してみる
view側(add.phtml)で実現したいこと
このadd.phtmlで実現したいことは、以下の通りです。
- WtiteControllerのaddActionメソッドから渡されるPostFormオブジェクトを使って入力フォームを表示させる(GET通信時:初めてこのaddページをリクエストする時)
- フォームにデータが入力されsubmitボタンが押された後、このadd.phtmlに入力したデータを渡す(POST通信時:submitをクリックした後のaddページ2回目のリクエスト時)
WriteControllerのaddActionメソッド側で、addページへのリクエストが、1回目のリクエスト(GET通信)か2回目のリクエスト(POST通信)なのかを判断し、処理内容を変更します。この部分は、後ほど解説します!
view側(add.phtml)を作成する!
module/Blog/view/blog/write/add.phtmlを作成し、以下の内容を実装します。
一つ注意なのが、add.phtmlのパスがlistではなく、writeになっています。
(コントローラの名前がそのディレクトリ名となります。今回の作成するadd.phtmlにデータを渡すのは、WriteControllerのaddActionメソッドであるためです。詳しくはこちらです。Components laminas-view : Controller and viewModel)
<h1>Add a blog post</h1>
<?php
$form = $this->form; // 1
$form->setAttribute('action', $this->url()); // 2
$fieldset = $form->get('post'); // 3
$title = $fieldset->get('title'); // 4
$title->setAttribute('class', 'form-control'); // 5
$title->setAttribute('placeholder', 'Post title'); // 6
$text = $fieldset->get('text');
$text->setAttribute('class', 'form-control');
$text->setAttribute('placeholder', 'Post content');
$submit = $form->get('submit'); // 7
$submit->setAttribute('class', 'btn btn-primary'); // 8
$form->prepare(); // 9
echo $this->form()->openTag($form); // 10
?>
<fieldset>
<div class="form-group">
<?= $this->formLabel($title) ?> // 11
<?= $this->formElement($title) ?> // 12
<?= $this->formElementErrors()->render($title, ['class' => 'help-block']) ?> // 13
</div>
<div class="form-group">
<?= $this->formLabel($text) ?>
<?= $this->formElement($text) ?>
<?= $this->formElementErrors()->render($text, ['class' => 'help-block']) ?>
</div>
</fieldset>
<?php
echo $this->formSubmit($submit); // 14
echo $this->formHidden($fieldset->get('id')); // 15
echo $this->form()->closeTag(); // 16
Laminas formコンポーネントのビューヘルパー側の使い方の概要は以下の流れになります。
- PostFormオブジェクトを呼び出す。
- setAttributeメソッドで、formタグやinputタグに必要な属性を設定する。
- prepareメソッドで、PostFormオブジェクトに設定した属性値を入れ込みます。
- openTagメソッドで、HTMLのform開始タグのを生成する。
- formLabelメソッドやformElementメソッドで、HTMLのlabelタグやinputタグを生成する。
- closeTagメソッドで、HTMLのform閉じタグを生成する。</form>
という流れになっています。以下の公式ドキュメントに詳細が載っています。
- Formオブジェクトの概要が確認できます。 Components laminas-form : Rendering
- formElementについてです。 Components laminas-form : FormElement
- formLabelについてです。 Components laminas-form : FormLabel
その他の要素も載っていますので、時間がある際に読んでみてください。
// 1 ~ // 16の解説
長いですが、// 1 ~ // 16を簡単に説明していきます。
// 1「$form = $this->form;」について
$form = $this->form;
WriteControllerのaddActionメソッドから渡されたオブジェクトの中からPostFormオブジェクトを取得し、変数$formに格納しています。
// 2「$form->setAttribute(‘action’, $this->url());」について
$form->setAttribute('action', $this->url());
PostFormオブジェクトに属性を設定します。(第一引数:セットする属性、第二引数:セットする内容)
上記のコードでは、下記のformタグのアンダーライン部分を追加したと同じ意味になっています。
※この時点ではまだformタグの生成はされていません。
ちなみにデフォルトでのmethodはpost通信となります。
<form action="このページのURL" method="post">
ちなみに、get通信の場合は、以下のように追記が必要です。
$form->setAttribute('action', $this->url());
$form->setAttribute('method', 'GET'); //追記
// 3「$fieldset = $form->get(‘post’); 」について
$fieldset = $form->get('post');
formオブジェクトから、getメソッドでPostFormオブジェクトを取得し、変数$fieldsetに格納します。
// 4「$title = $fieldset->get(‘title’);」について
$title = $fieldset->get('title');
// 3で取得したPostFormオブジェクトからtitleのform部分を取得します。その後、変数$titleに格納します。
// 5 $title->setAttribute(‘class’, ‘form-control’);について
$title->setAttribute('class', 'form-control');
PostFormオブジェクトの中のtitle部分に「form-control」というclass属性を追加しています。
// 6「$title->setAttribute(‘placeholder’, ‘Post title’);」について
$title->setAttribute('placeholder', 'Post title')
PostFormオブジェクトのtitle部分にplaceholder属性に「Post title」を設定しています。
PostFormオブジェクトのtext部分も同様に、class属性に「form-control」、placeholder属性に「Post content」を追加します。
title部分と同じ内容なので、説明は割愛します。
// 7「$submit = $form->get(‘submit’); 」について
$submit = $form->get('submit');
PostFormオブジェクト内のsubmitを取得し、変数$submit内に格納しています。
// 8「$submit->setAttribute(‘class’, ‘btn btn-primary’);」について
$submit->setAttribute('class', 'btn btn-primary');
上記で取得したsubmitにclass属性を追加し、属性値として”btn btn-primary”を追加しています。
// 9「$form->prepare();」について
$form->prepare();
このPostFormオブジェクトのprepareメソッドを実行することで、今まででsetAttributeメソッドで設定した属性と属性値がこのオブジェクトに入れ込まれます。
ここまでで、formの下準備が完了しました!
ここからは、PostFormオブジェクトをHTMLとして生成していきます!
// 10「echo $this->form()->openTag($form);」について
echo $this->form()->openTag($form);
formの開始タグを生成します。
属性は//2の部分で設定した「action=”このページのURL”」が設定されています。以下のようになります。
<form action="このページのURL" method="post">
// 11「$this->formLabel($title)」について
$this->formLabel($title)
labelタグを生成します。
formLabelメソッドで、PostFormオブジェクトのtitle部分に設定したclass属性などをLabelタグに設定します。以下のようなLabelタグが生成されます。
<label class="form-control">
//12「$this->formElement($title)」について
$this->formElement($title)
formElementメソッドで、PostFormオブジェクトのtitleに設定したclass属性とplaceholder属性を設定し、以下のようなinputタグが生成されます。
<input type="text" name="title" class="form-control" placeholder="Post content" >
// 13「$this->formElementErrors()->render($title, [‘class’ => ‘help-block’])」について
$this->formElementErrors()->render($title, ['class' => 'help-block'])
formElementErrorsメソッドからのrenderメソッドという流れのチェーンメソッドで、PostFormオブジェクトのtitle部分にバリデーションエラーメッセージ表示用のulタグとliタグを生成します。
バリデーションエラーが発生した場合、以下のようなulタグが生成されます。
<ul class="help-block"><li>Value is required and can't be empty</li></ul>
//14「echo $this->formSubmit($submit);」について
echo $this->formSubmit($submit);
formSubmitメソッドで、PostFormオブジェクトのsubmit部分に設定した属性を設定し、以下のinputタグを生成します。
<input type="submit" class="btn btn-primary" name="submit" value="Insert new Post">
name属性とvalue属性は、PostForm.php作成時につけているものです。
//15「echo $this->formHidden($fieldset->get(‘id’));」について
echo $this->formHidden($fieldset->get('id'));
PostFormオブジェクトに含まれるPostFieldsetオブジェクト内のid部分をinputタグのhiddenに設定しinputタグのhiddenタイプを生成します。以下のようなinputタグになります。
<input type="hidden" name="id" value="">
//16「echo $this->form()->closeTag();」について
echo $this->form()->closeTag();
formの閉じタグを生成します。以下のタグです。
</form>
ここまでで、view側(add.phtml)の設定が完了しました!
次は、WriteControllerのaddActionメソッド内を修正します!
Formから入力されたデータをDBに保存できるようにコントローラーを修正する!
今のままでは、上記で作成したblog記事の入力フォームにタイトルや記事内容を入力しても、データベースに保存されません。
WriteControllerのaddActionメソッドを以下のような一般的なフォーム処理ロジックに変更します。
addActionメソッドの処理ロジックについて
以下のフローチャートのようになります。
WriteControllerのaddActionメソッド実装内容は以下の通りです。
module/Blog/src/Controller/WriteController.phpのaddActionメソッド
public function addAction()
{
// ①
$request = $this->getRequest();
$viewModel = new ViewModel(['form' => $this->form]);
if (! $request->isPost()) {
return $viewModel;
}
// ②
$this->form->setData($request->getPost());
if (! $this->form->isValid()) {
return $viewModel;
}
// ③
$data = $this->form->getData()['post'];
$post = new Post($data['title'], $data['text']);
// ④
try {
$post = $this->command->insertPost($post);
} catch (\Exception $ex) {
// An exception occurred; we may want to log this later and/or
// report it to the user. For now, we'll just re-throw.
throw $ex;
}
return $this->redirect()->toRoute(
'blog/detail',
['id' => $post->getId()]
);
}
上から順に簡単に説明します。
// ①「POST通信かどうかの確認」部分の実装
$request = $this->getRequest();
$viewModel = new ViewModel(['form' => $this->form]);
if (! $request->isPost()) {
return $viewModel;
}
この実装は、以下のような流れになっています。
- getRequestメソッドからリクエストオブジェクトを取得し、変数$requestに格納する
- インスタンス化したviewModelオブジェクトに、デフォルトのformオブジェクト(PostForm)を設定し、変数$viewModelに格納
- if文で、リクエストがPOST通信でない場合は、デフォルトのformオブジェクトが含まれるviewModelオブジェクトをview側に渡す。(isPostメソッドは、継承元のAbstractController内の使用するHttpRequestオブジェクトの中にあるメソッドのこと)
参考までに、「getRequestメソッド」は、このコントローラの継承元のAbstractControllerに含まれるメソッドのことです。
// ②「入力されたフォームのデータを検証する」部分の実装
$this->form->setData($request->getPost());
if (! $this->form->isValid()) {
return $viewModel;
}
この実装は、以下のような流れになっています。
- リクエストのPOST通信のデータを取得し、そのデータをPostFormオブジェクトにセットする
- データがセットされたPostFormオブジェクトで、isValidメソッドを実行し、検証する
- 検証が失敗なら、viewModelオブジェクトをview側に渡す(検証エラーメッセージも含まれたviewModelオブジェクトを渡している)
参考までに「setDataメソッド」と「isValidメソッド」は、PostFormクラスの継承元のFormクラスに含まれています。
// ③「検証に成功したデータをPostエンティティに格納する」部分の実装
$data = $this->form->getData()['post'];
$post = new Post($data['title'], $data['text'])
この部分の実装は以下のような流れになっています。
- PostFormオブジェクトからgetDataメソッドで、検証に成功したPostFormオブジェクト内の[‘post’]部分のデータを、$data変数に格納する
- 1で取得したデータのtitle部分とtext部分を、インスタンス化したPostエンティティに注入し、$post変数に格納する
参考までに「getDataメソッド」は、PostFormクラスの継承元のFormクラスに含まれています。
// ④「try catch構文でDBにデータを保存し、詳細ページへ遷移」部分の実装
try {
$post = $this->command->insertPost($post);
} catch (\Exception $ex) {
// 例外処理です。今は一旦、エラー内容をそのまま返しています。
throw $ex;
}
return $this->redirect()->toRoute(
'blog/detail',
['id' => $post->getId()]
);
この部分の実装について、上から順に説明します。
- try catch構文で、LaminasDbSqlCommandインスタンスからinsertPostメソッドで、データベースにPostエンティティに格納されているtitleとtextを保存し、insertPostメソッドの戻り値である「付与されたidとtiltleとtextを含むPostエンティティ」を変数$postに格納する
- redirectメソッドで、遷移先のURLを渡す(遷移先はtoRouteメソッドで、routeで指定します。detailページは、idが必要なので、今回データベースに保存したPostエンティティのidを渡します。)
ここまでで、WriteControllerのaddActionメソッドの修正が完了しました!これでブラウザに表示させることが出来ます!
blogをブラウザに表示してみる
簡単に手順を説明します。手順は、Docker Desktopを使用した場合です。(Macの場合です)
その他の場合は、こちらのページに詳細があります。Components tutorials : Web Servers
- Docker Desktopを起動します。
- ターミナルを立ち上げて、Laminasのこれまで実装してきたコードがあるディレクトリで、docker-compose.ymlファイルがあるディレクトリに移動します。
- コマンドで「docker-compose up -d –build」を実行します。
- http://localhost:8080/blogをブラウザで開きます。
以下のような画面が表されます。
blogトップページ(/blog)
blog追加ページ(/blog/add)
blog詳細ページ(blog/{id})
お疲れ様でした!
次は、laminas-hydratorを使用し、addActionメソッドを一部分を修正します!
コメント