RitoLabo

CakePHP3で独自フォームテンプレートをWidgetクラスで作成する(独自ウィジェット)

  • 公開:
  • 更新:
  • カテゴリ: PHP CakePHP
  • タグ: PHP,Form,CakePHP,3.5,FormHelper,WidgetClass,FormTemplate,DatePicker

今回は、CakePHP3のHTMLフォームパーツのテンプレートを、Widgetクラスを用いて独自に作成していきます。

アジェンダ
  1. 開発環境
  2. Widgetクラス
  3. Widgetクラス作成
  4. 独自フォームテンプレート作成
  5. フォームテンプレートとウィジェット読み込み
  6. FormHelperで描画
  7. 動作確認

開発環境

今回の開発環境は以下の通りです。

  • 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' => 'サンプル']]);

動作確認

実装が完了したのでブラウザから確認してみます。

WidgetクラスによるDatePickerフォーム

フォームテンプレートからDatePickerを呼び出せました。

まとめ

以上で作業は完了です。

通常のフォームのみを使っていればそんなに気にならない部分ですが、例えばJavaScriptを使ってインタラクティブなフォームを作成する場合などは、どうしてもフォームの使い方がエクセントリックになったり複雑なid/class/data属性になりがちなので、そんな時はウィジェットで独自にフォームパーツを作成すると作業もはかどると思いますので、是非試してみてください。