1. Home
  2. PHP
  3. CakePHP
  4. CakePHP3で独自フォームテンプレートをWidgetクラスで作成する(独自ウィジェット)

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

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

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

Contents

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

動作確認

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

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

まとめ

以上で作業は完了です。

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

Author

rito

  • Backend Engineer
  • Tokyo, Japan
  • PHP 5 技術者認定上級試験 認定者
  • 統計検定 3 級