CakePHP3で独自フォームテンプレートをWidgetクラスで作成する(独自ウィジェット)
- 公開日
- 更新日
- カテゴリ:CakePHP
- タグ:PHP,Form,CakePHP,FormHelper,WidgetClass,FormTemplate,DatePicker

今回は、CakePHP3 の HTML フォームパーツのテンプレートを、Widget クラスを用いて独自に作成していきます。
Contents
開発環境
今回の開発環境は以下の通りです。
- Linux CentOS 7
- Apache 2.4
- PHP 7.2
- CakePHP 3.5
CakePHP のルートディレクトリを「cakephp/」とします。
Widget クラス
CakePHP3 のフォームテンプレートと言えば、FormHelper から利用する HTML フォームパーツのテンプレートの事を指しますが、それらは各タイプ(input/select/radio etc...)ごとに作成されているので融通が利かない場合があります。
例えば、同じ input だけど別のテンプレートを当てたい。みたいな時です。
そんな時は、Widget クラスを作成する事で、独自のフォームテンプレートを定義する事が出来ます。
ちなみに通常のテキスト入力といえば input タイプ「text」になりますが、今回は同じタイプでも別のフォームテンプレートを参照できるようにします。
そこで、今回もう一方の input には bootstrap のDatePicker をあてたいと思います。 DatePicker は日時を扱うものですが、入力自体は input で行われるので、通常の input のテンプレートだと基本的にはレイアウトが崩れてしまいます。
こいつを別テンプレートで表示させていきます。
Widget クラス作成
Widget クラスを作成し定義していきます。
cakephp/src/View 配下に Widget ディレクトリを作成し、その配下に DatePickerWidget.php を作成します。
cakephp
├─ src
│ ├─ View
│ │ ├─ Widget
│ │ │ └─ DatePickerWidget.php
作成した DatePickerWidget.php を Widget クラスとして、以下のように記述します。
- cakephp/src/View/Widget/DatePickerWidget.php
-
<?php namespace App\View\Widget; use Cake\View\Form\ContextInterface; use Cake\View\Widget\WidgetInterface; class DatePickerWidget implements WidgetInterface { protected $_templates; public function __construct($templates) { $this->_templates = $templates; } public function render(array $data, ContextInterface $context) { $data += [ 'name' => '', 'val' => '', ]; return $this->_templates->format('datepicker', [ 'name' => $data['name'], 'val' => $data['val'], 'templateVars' => $data['templateVars'], 'attrs' => $this->_templates->formatAttributes($data, ['name'], ['val']) ]); } public function secureFields(array $data) { return [ $data['name']]; } }
上から説明していきます。
Widget class の作成時は、WidgetInterface の実装が義務付けられている為、implements して必要な render() メソッドと secureFields() メソッドの実装を行います。
render() メソッドに関しては、入ってきた入力情報などをどう扱い、テンプレートへ渡すかを定義しています。ここで操作したフォームパーツ情報や入力データは、ウィジェットの HTML へ文字列として渡す事ができます。
ちなみに return する時に、format() の第二引数に各値をセットしていますが、第一引数には独自ウィジェット、要するに HTML テンプレートの識別子を指定します。
secureFields() メソッドに関しては、保護するフィールドのリストを渡しています。
今回は DatePicker のデモなのでこんな感じになっていますが、ひとまず試してみたいという場合は、上記ソースのクラス名の変更と、render() メソッドで return する時の、format() の第一引数の名前(例では「datepicker 」としている部分)を任意のものに変えてもらえれば、そのまま使えます。
独自フォームテンプレート作成
次は、独自の HTML フォームパーツのテンプレート作成です。
cakephp/config 配下に Template ディレクトリを作成し、その配下に form-templates.php(ファイル名は任意で OK) を作成します。
cakephp
├─ config
│ ├─ Templates
│ │ └─ form-templates.php
作成したファイルに、DatePicker ウィジェットのフォームテンプレートを定義していきます。
- cakephp/config/Template/form-templates.php
-
<?php return [ /** * 通常のフォームテンプレート */ 'formStart' => '<form class="form-horizontal" {{attrs}}>', 'hiddenBlock' => '<div style="display:none;">{{content}}</div>', 'inputContainerError' => '<div class="form-group has-error"><label class="col-sm-2 control-label" {{attrs}}>{{label}}</label><div class="col-sm-10">{{content}}{{error}}</div></div><div class="hr-line-dashed"></div>', 'inputContainer' => '<div class="form-group"><label class="col-sm-2 control-label" {{attrs}}>{{label}}</label><div class="col-sm-10">{{content}}</div></div><div class="hr-line-dashed"></div>', 'label' => '<label class="col-sm-2 control-label" {{attrs}}>{{text}}</label>', 'input' => '<input class="form-control" type="{{type}}" name="{{name}}" {{attrs}}>', 'textarea' => '<textarea name="{{name}}" class="form-control" {{attrs}}>{{value}}</textarea>', 'select' => '<select name="{{name}}" class="chosen-select" tabindex="2">{{content}}</select>', 'option' => '<option value="{{value}}"{{attrs}}>{{text}}</option>', 'button' => '<div class="form-group"><div class="col-sm-4 col-sm-offset-2"><button type="{{type}}" class="btn btn-primary" {{attrs}}>{{text}}</button></div></div>', /** * Widgetクラス DatePickerWidget 独自テンプレート */ 'datepicker' => '<div class="input-group date"><span class="input-group-addon"><i class="fa fa-calendar"></i></span><input type="text" value="{{value}}" class="form-control" name="{{name}}" data-date-format="yyyy-mm-dd" {{attrs}}></div>' ];
最下部に先ほど作成した DatePicker ウィジェットのフォームテンプレートを定義しています。
例では通常のフォームテンプレートも記述していますが、こうして混合で記述も出来るよと言いたいだけなので、不要ならば実装は不要です。
通常のフォームテンプレートには無い「datepicker 」という定義を見ると、なんだかわくわくしてきます。
フォームテンプレートとウィジェット読み込み
作成したものをコントローラで読み込み、使えるようにします。適当なコントローラに、以下の記述を行い、テンプレートとウィジェットを読み込ませます。
class SampleController extends AppController
{
public $helpers = [
'Form' => [
'templates' => 'Templates/form-templates',
'widgets' => [
'datepicker' => ['DatePicker']
]
],
];
メンバ変数 $helpers に、フォームプロパティに対してそれぞれテンプレートとウィジェットを指定している事がわかると思います。これで先ほど作成したテンプレートとウィジェットが読み込まれます。
FormHelper で描画
最後に、ビューテンプレート内で FormHelper を使ってフォームを描画します。以下のようにして、type に datepicker を指定してあげるだけです。その他は通常の使い方と変わりません。
echo $this->Form->control('sample_date_at', ['type' => 'datepicker', 'label' => false, 'templateVars' => ['label' => 'サンプル']]);
動作確認
実装が完了したのでブラウザから確認してみます。
フォームテンプレートから DatePicker を呼び出せました。
まとめ
以上で作業は完了です。
通常のフォームのみを使っていればそんなに気にならない部分ですが、例えば JavaScript を使ってインタラクティブなフォームを作成する場合などは、どうしてもフォームの使い方がエクセントリックになったり複雑な id/class/data 属性になりがちなので、そんな時はウィジェットで独自にフォームパーツを作成すると作業もはかどると思いますので、是非試してみてください。