Yii でチェックボックスの値を複数同時に保存する方法

## はじめに
Yii でチェックボックスを用意して、複数選択後、それをすべてデータベースに保存する方法です。
いろいろ調べて見たんですが、これだというものがなかったので、自分なりに書いてみました。

## テーブルの用意

~~~point.sql
CREATE TABLE `point` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `score` int(11) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8
~~~

例として point テーブルを作成して、id, score というカラムを用意。
score は数値のみを許可して、チェックボックスで選択された 1 から 5 の値を保存する、という形を取ります。

## モデル

~~~Point.php
class Point extends ActiveRecord
{
    ...
    public function rules()
    {
        return array(
            // score
            array('score', 'required'),
            array('score', 'in', 'range'=>range(1, 5)),
        );
    }
    ...
~~~

score カラムはどれか必ず選択しなくてはいけないのと、1 から 5 の数値のみを許可します。

## コントローラー

~~~PointController.php
class PointController extends Controller
{
    ...
    public function actionCreate()
    {
        $point = new Point();
        if (isset($_POST['Point'])) {
            $point->attributes = $_POST['Point'];

            $scores = $point->score;
            $valid = true;

            if (is_array($scores)) {
                foreach ($scores as $score) {
                    $point->score = $score;
                    $valid = $point->validate() && $valid;
                }
                if ($valid) {
                    $transaction = Yii::app()->db->beginTransaction();
                    try {
                        foreach ($scores as $score) {
                            $point->setIsNewRecord(true);
                            $point->setPrimaryKey(null);
                            $point->score = $score;
                            $point->save(false);
                        }
                        $transaction->commit();
                        $this->redirect(array('index'));

                    } catch (Exception $e) {
                        $transaction->rollBack();
                    }
                }
            } else {
                $point->validate();
            }
        }
        $this->render('_form', compact('point'));
    }
}
~~~

$_POST['Point']['score'] は配列になっているため、バラして、一つ一つバリデーションを通して、OK なら保存するという形になります。保存する際に、setIsNewRecord(true), setPrimaryKey(null) などで id をリセットしないと save の 2 回目から Update になるので注意が必要です。

## ビュー

~~~_form.php
<div class="form">
    <?php echo CHtml::form(); ?>
    <?php echo CHtml::errorSummary($point); ?>

    <div class="row">
        <?php echo CHtml::activeLabel($point, 'score'); ?>
        <?php $pointOptions = array(1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5); ?>
        <?php echo CHtml::activeCheckBoxList($point, 'score', $pointOptions, array(
            'labelOptions' => array('style' => 'display: inline;'),
        )); ?>
    </div><!-- /.row -->

    <div class="row">
        <?php echo CHtml::submitButton($point->isNewRecord ? 'Create' : 'Update'); ?>
    </div><!-- /.row -->

    <?php echo CHtml::endForm(); ?>
</div><!-- /.form -->
~~~

CHtml::activeCheckBoxList() でチェックボックスを複数用意します。
これで複数選択された場合は、選択された値が n 個保存されるようになります。

## まとめ

とりあえず、わりと強引な方法ではありますが、チェックボックスを使った複数保存の方法を書いてみました。
値をバラしてバリデーションして、値をバラして一つ一つ保存する、という形がスマートではないので、他に良い方法を知っている方はぜひ教えてください。

また Yii のバージョン 1.1.14 で [createMultipleInsertCommand](http://www.yiiframework.com/doc/api/1.1/CDbCommandBuilder#createMultipleInsertCommand-detail) が追加され、foreach でまわして数回 INSERT するのではなく、1 回のクエリでできるようになったみたいですが、値を連想配列にしないといけないとかでこの記事では採用しませんでした。