symfony1.4+Doctrineで配列をカンマ区切りで保存する

最近チェックボックスの項目が大量あるフォームを実装していて、綺麗に正規化して実装するのも大変だなーと思って、valueを「,」区切りで文字列保存することにしたのでメモ。
valueに「,」が入る可能性などは考えてないのであくまでもシンプルに。

config/doctrine/scheme.yml

schemeの定義例は以下。interestに「,」区切りで保存する。

Question:
  columns:
    name: string(255)
    interest: string(255)

config/ProjectConfiguration.class.php

sfDoctrineRecordを継承して、myDoctrineRecordを定義するのでそちらをmodel生成で読み込むように。

<?php

require_once dirname(__FILE__).'/../lib/vendor/symfony/lib/autoload/sfCoreAutoload.class.php';
sfCoreAutoload::register();

class ProjectConfiguration extends sfProjectConfiguration
{
  public function setup()
  {
    $this->enablePlugins('sfDoctrinePlugin');
  }

  public function configureDoctrine(Doctrine_Manager $manager)
  {
    sfConfig::set('doctrine_model_builder_options', array(
      'baseClassName' => 'myDoctrineRecord',
    ));
  }
}

lib/doctrine/myDoctrineRecord.class.php

implode, explodeするメソッドを定義。

<?php

class myDoctrineRecord extends sfDoctrineRecord
{
  protected function _getExplode($key, $separator = ',', $load = true)
  {
    $value = $this->_get($key, $load);
    if ($value === '' || $value === null) {
      return array();
    }
    return array_map('trim', explode($separator, $value));
  }

  protected function _setImplode($key, $value, $separator = ',', $load = true)
  {
    if (is_array($value)) {
      $this->_set($key, implode($separator, array_map('trim', $value)), $load);
    } else {
      $this->_set($key, '', $load);
    }
  }
}

コマンドラインでモデルとCRUD画面生成

$ symfony doctrine:build --all
$ symfony doctrine:generate-module frontend question question

lib/model/doctrine/Question.class.php

interestのgetter,setterは前述のmethodを通すようにする。

<?php

class Question extends BaseQuestion
{
  public function getInterest()
  {
    return $this->_getExplode('interest');
  }

  public function setInterest($value)
  {
    $this->_setImplode('interest', $value);
  }
}

lib/model/doctrine/QuestionTable.class.php

interestの定義

<?php

class QuestionTable extends Doctrine_Table
{
  protected static $interests = array(
    '1' => 'Music',
    '2' => 'Sports',
    '3' => 'Programming',
  );

  public static function getInterests()
  {
    return self::$interests;
  }

  public static function getInstance()
  {
    return Doctrine_Core::getTable('Question');
  }
}

lib/form/doctrine/QuestionForm.class.php

intersetのwidget,validatorをQuestionTable::getInterests()にする。

<?php

class QuestionForm extends BaseQuestionForm
{
  public function configure()
  {
    $intersets = QuestionTable::getInterests();
    $this->setWidget('interest', new sfWidgetFormChoice(array(
      'choices'  => $intersets,
      'multiple' => true,
      'expanded' => true,
    )));
    $this->setValidator('interest', new sfValidatorChoice(array(
      'choices'  => array_keys($intersets),
      'multiple' => true,
    )));
  }
}

とするとこうなって、新規作成・編集することができる。


getter,setterをオーバライドしているので、form側で特に意識することなくデフォルト値のセット、データの保存ができて便利かなと思っています。

他のプロジェクトでも使えそうな気がするので、scheme.ymlに書いたら自動的にやってくれるようにDoctrineのBehaviorにしてみたい。