CakePHP 3にて、Formヘルパーのcontrol()
メソッドを使いdatetime型
の入力フィールドを生成する際に、div
エレメントで括らないselect
エレメントを生成させる方法を検討してみます。
datetime型のウィジェット?
CakePHP3で遊ぶ - bootstrapに合わせたFormヘルパーのtemplate -では、templateをカスタマイズして任意のdiv
エレメントを出力させる方法を検討しました。しかし、datetime型の入力フィールドは、複数のselect
エレメントを出力するので、templateだけでは思うように入力フィールドを生成できません。
そこで、datetime型の入力フィールドをどのように生成しているのか確認してみます。
CakePHP 3では、Formヘルパーのcontrol()
メソッドを使って入力フィールドを生成する場合、それぞれのエレメントに対応したウィジェットが入力フィールドを生成します。
ならば、datetime型の入力フィールドを生成しているウィジェットをカスタマイズしてみればよさそうです。
まずは、標準のウィジェットがどのようになっているのか確認してみます。
FormHelper.php
protected $_defaultConfig = [ : 'templates' => [ : // Widget ordering for date/time/datetime pickers. 'dateWidget' => '{{year}}{{month}}{{day}}{{hour}}{{minute}}{{second}}{{meridian}}', : // Select element, 'select' => '<select name="{{name}}"{{attrs}}>{{content}}</select>', : ] ]; protected $_defaultWidgets = [ : 'select' => ['SelectBox'], : 'datetime' => ['DateTime', 'select'], : ];
ここからわかることは、datetime型の入力フィールドを生成しているウィジェットは、DateTimeWidget
が処理をしていて、さらにSelectBoxWidget
を呼び出していることです。
SelectBoxWidget
は、select
エレメントを生成しているウィジェットです。このことからも、datetime型の入力フィールドをselect
エレメントで構築していることがわかります。
独自ウィジェットに差し替える
そこで、datetime型の入力フィールドを生成させるウィジェットを新たに作成することにします。
やりたいことは、div
エレメントで括らないselect
エレメントを生成させるSelectBoxWidget
を継承したウィジェットを作成します。
View/Widget/AppSelectBoxWidget.php
<?php namespace App\View\Widget; use Cake\View\Form\ContextInterface; use Cake\View\Widget\SelectBoxWidget; class AppSelectBoxWidget extends SelectBoxWidget { public function render(array $data, ContextInterface $context) { $data += [ 'name' => '', 'empty' => false, 'escape' => true, 'options' => [], 'disabled' => null, 'val' => null, 'templateVars' => [] ]; $options = $this->_renderContent($data); $name = $data['name']; unset($data['name'], $data['options'], $data['empty'], $data['val'], $data['escape']); if (isset($data['disabled']) && is_array($data['disabled'])) { unset($data['disabled']); } $template = 'selectNoDiv'; $attrs = $this->_templates->formatAttributes($data); return $this->_templates->format($template, [ 'name' => $name, 'templateVars' => $data['templateVars'], 'attrs' => $attrs, 'content' => implode('', $options), ]); } }
30行目に$template = 'selectNoDiv';
としています。ここで、使用するtemplateを指定しています。
次に、このウィジェットを使うようにaddWidget()
します。
今回も、viewで変更することにします。
$this->Form->addWidget('appselect', ['AppSelectBox']); $this->Form->addWidget('datetime', ['DateTime', 'appselect']);
ここで、appselect
という識別名でAppSelectBox
ウィジェットを追加します。
さらに、datetime
をappselect
に置き換えます。
以上で、datetime型の入力フィールドを生成する際にAppSelectBoxWidget
が使われるようになりました。
つぎに、AppSelectBoxWidget
で参照するselectNoDiv
templateを追加しておきます。
$this->Form->templates([ : 'dateWidget' => '<div class="col-xs-6">{{year}} {{month}} {{day}} {{hour}} {{minute}} {{second}} {{meridian}}</div>', : 'select' => '<div class="col-xs-6"><select name="{{name}}"{{attrs}}>{{content}}</select></div>', : 'selectNoDiv' => '<select name="{{name}}"{{attrs}}>{{content}}</select>', ]);
この状態で
echo $this->Form->control('field');
とすると目的の通りに、年、月、日などの入力フィールドがselect
エレメントで作成されますが、個々のselect
エレメントにはdiv
エレメントに括られていない状態で生成されるようになりました。