はじめてのウィジェット

YiiFramework.comでは、毎日のようにエクステンションが登録されたり、チュートリアルが投稿されています。眺めていると使えそうなものや、アイデアに出会えたりして楽しいですよね。

さて、今回は[エクステンション](http://www.yiiframework.com/doc/guide/1.1/ja/extension.overview)を自作してみます。一口にエクステンションといってもいろいろありますが、[jQuery UI MultiSelect Widget](http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/)をYiiFrameworkのウィジェットにします。

## ウィジェットを作る前に

ビューで直接jQuery UI MultiSelect Widgetを使ったコードです。

``` php:view.php
MultiSelect on View
<?php 
// データ
$data = array(
    1=>'Yii',
    2=>'Advent',
    3=>'Calendar',
    4=>'2012',
);
$select = array(1, 3);

// 依存する JavaScript,CSS をインクルードする （一部）
$url = Yii::app()->getAssetManager()->publish($assets_dir.DIRECTORY_SEPARATOR.'jquery.multiselect.js');
Yii::app()->getClientScript()->registerScriptFile($url, CClientScript::POS_END);

// SELECTタグ出力
echo CHtml::dropDownList('multiselect',$select,$data,array(
    'id'=>'multiselect',
    'multiple'=>'multiple',
));

// SELECTタグにjquery.multiselectを適用する
Yii::app()->getClientScript()->registerScript('#multiselect', "$('#multiselect').multiselect();");
?>
```

なんかいろいろやっててとてもビューに見えません。これでもJavaScript、CSSのインクルードを省略しているんですが。。。えーと、他のビューで同じマルチセレクトを使いたいとき、これらをコピペするのでしょうか？！

**いいえ、YiiFrameworkではウィジェット化するんです！**

> ウィジェットは、ユーザインタフェースコードの再利用性を高めてくれます。
> http://www.yiiframework.com/doc/guide/1.1/ja/basics.view#sec-3

![MultiSelect on View](https://lh3.googleusercontent.com/-qyrGgL3HdJ0/UM82yJJPwhI/AAAAAAAAAGk/UdvJbpqJfs4/s377/MultiSelect.png "MultiSelect on View")

## ウィジェットを作る

jQuery UIウィジェットのYiiウィジェット化は、決して難しくありません。どのjQuery UIウィジェットでも、最低限やるべきことは3つです。

1. 依存するjsファイル、cssファイルをインクルードする
2. ウィジェットに必要なHTMLを出力する
3. オプションをjQuery UIウィジェット設定インタフェースに渡す

そして作成したウィジェットはこちら。
[EMultiSelectWidget.php](https://gist.github.com/4318997/)

## EMultiSelectWidgetを使う

EMultiSelectWidgetを使うと、ビューはこのようにすっきりします。

```php:view.php
EMultiSelectWidget :
<?php 
$this->widget('ext.emultiselectwidget.EMultiSelectWidget', array(
    'name'=>'EMultiSelectWidget',
    'data'=>$data,
    'select'=>$select,
));
```

`$data`と`$select`は省略しましたが、上のコードと同じです。

## EMultiSelectWidgetの設定

`options` から[jQuery UI MultiSelect Widget](http://www.erichynds.com/jquery/jquery-ui-multiselect-widget/)のオプションを設定できます。CJavaScriptExpressionを使ってJavaScriptも注入できる！jQueryウィジェットとYiiウィジェットって相性いいなあ。
`htmlOptions`から出力するSELECTタグの属性を設定できます。

```php:view.php
EMultiSelectWidget :
<?php 
$this->widget('ext.emultiselectwidget.EMultiSelectWidget', array(
    'name'=>'EMultiSelectWidget',
    'data'=>$data,
    'select'=>$select,
    'options'=>array(
    	'checkAllText'=>'すべて選択',
    	'uncheckAllText'=>'選択解除',
    	'noneSelectedText'=>'選択してください',
    	'selectedText'=>'#つ選択中',
    	'click'=>new CJavaScriptExpression('function(){console.log(\'click!!\')}'),
    )
    'htmlOptions'=>array(
        'id'=>'hogeid',
        'class'=>'piyoclass',
    )
));
```

[Yii Advent Calendar 2012](http://qiita.com/advent-calendar/2012/yii)でも何度か触れられていますが、CWidgetFactoryにまとめて設定することで、使う度に設定する手間も省けます。

![EMultiSelectWidget](https://lh6.googleusercontent.com/-9zYxfN00O4k/UM82pz9rqRI/AAAAAAAAAGc/HdDJfC9PAqs/s400/EMultiSelectWidget.png "EMultiSelectWidget")

## まとめ

PHP+HTML+JavaScript+CSSを一括りにするときはウィジェット化しよう。ウィジェット単体でテストできるし、再利用もできる。

## もうひとつ - 思いつき

ウィジェット化すると、PHP+HTML+JavaScript+CSSが一括りにできて便利なのですが、HTMLとしては読みにくくなります。そこで思いついたのですが、次だとどうでしょう。

```php:view.php
<?php $w = $this->beginWidget("multiselect"); ?>
<select id="<?php echo $w->id; ?>" class="...">
  <?php echo CHtml::listOptions($select, $data); ?>
</select>
<?php $this->endWidget(); ?>
```

未検証ですが、次のメリットがあるかなーと。

* select タグを使っていることを明確にできる
* select タグの属性を、`array('htmlOptions'=>)`でなく、HTMLに書ける