CakePHP3で独自フォームテンプレートをWidgetクラスで作成する(独自ウィジェット)
- 公開:
- 更新:
- カテゴリ: PHP CakePHP
- タグ: PHP,Form,CakePHP,3.5,FormHelper,WidgetClass,FormTemplate,DatePicker
今回は、CakePHP3のHTMLフォームパーツのテンプレートを、Widgetクラスを用いて独自に作成していきます。
開発環境
今回の開発環境は以下の通りです。
- 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属性になりがちなので、そんな時はウィジェットで独自にフォームパーツを作成すると作業もはかどると思いますので、是非試してみてください。