MySQLのCLIのプロンプトをカスタマイズするzshのfunctionを書いた
いままで、mysqlのプロンプトの変更は $HOME/.my.cnf で、
[mysql]
prompt="^[[01;31m\U[\d]-[\D]^[[0m\nmysql> "
みたいにしていましたが、
- 接続元のサーバ情報が出せない。(出し方を知らないだけかも)
- 本番と開発環境など、同一の設定ファイルで環境に応じてプロンプトを柔軟に変更できない。
- 色をつけるにはエスケープシーケンスを直書きしないといけない。
といった点が、ちょっと不満でした。
なのでコマンドオプションをパースして、環境変数$MYSQL_PS1を組み立ててから
mysqlコマンドを実行する、zshのfunctionを作ってみました。
https://github.com/tetsujin/zsh-function-mysql
ちょっとわかりにくいですが、下記の例では
- 接続元のサーバ情報を表示、ホスト名によって色分け。
- 接続先のMySQLサーバのユーザ名、ホスト名で色分け。
をしています。
インストール
zshが読み込める場所にfunctionを追加します。
この例ではホームディレクトリに追加します。
$ cd /path/to $ git clone git://github.com/tetsujin/zsh-function-mysql.git $ mkdir -p ~/.zsh.d/functions $ ln -s /path/to/zsh-function-mysql/mysql ~/.zsh.d/functions
$HOME/.zshrcへ設定を追記
インストールしたfunctionと、色設定で使うcolorsをautoloadします。
typeset -U fpath fpath=( $HOME/.zsh.d/*(/N) $fpath ) autoload -U colors; colors autoload -U $(echo ~/.zsh.d/functions/*(:t))
下記のように設定を追記します。
# MySQLの接続元ホストのユーザ名($USER)によって色分けします。(自分は使ってません) typeset -A mysql_prompt_style_client_user mysql_prompt_style_client_user=( # 'root' $fg_bold[red] # '*' $fg_bold[green] ) # MySQLの接続元ホスト名($HOST)によって色分けします。 # - .local.を含むローカル環境は緑 # - .dev.を含む開発環境では黄色 # - その他は赤 # としています。 typeset -A mysql_prompt_style_client_host mysql_prompt_style_client_host=( '*.local.*' "$fg_bold[green]" '*.dev.*' "$fg_bold[yellow]" '*' "$fg_bold[red]" ) # MySQLの接続先ホストのユーザ名(-uオプション)によって色分けします。 # - rootは赤 # - その他は青 # としています。 typeset -A mysql_prompt_style_server_user mysql_prompt_style_server_user=( 'root' "$bg_bold[red]$fg_bold[yellow]" '*' "$fg_bold[blue]" ) # MySQLの接続先ホスト名(-hオプション)によって色分けします。 # - masterを含む場合は赤 # - slaveを含む場合は黄色 # - その他は青色 # としています。 typeset -A mysql_prompt_style_server_host mysql_prompt_style_server_host=( '*master*' "$bg_bold[red]$fg_bold[yellow]" # Master Server '*slave*' "$bg[yellow]$fg[black]" # Slvae Server '*' "$fg_bold[blue]" ) # プロンプトを組み立てます。 # [注意] functionの実行直前に変数を評価するのでシングルクォートで定義してください。 mysql_prompt='${style_client_host}${USER}@${HOST}${fg_bold[white]} -> ' mysql_prompt=$mysql_prompt'${style_server_user}\u${reset_color}${fg_bold[white]}@${style_server_host}\h${reset_color}${fg_bold[white]}:${fg[magenta]}\d ${fg_bold[white]}\v\n' mysql_prompt=$mysql_prompt'${fg_bold[white]}${bg_level}mysql${reset_color}> '
iTerm2とTerminal.appで軽く動作確認しただけなので、正しく表示できない環境もあるかもです。
視覚的にわかりやすくすることで、少しでもオペレーションミスが減れば嬉しいですね。
CoffeeScript + Guard::CoffeeScript + Emacs + Flymakeで「Errno::ENOENT: No such file or directory」が出ないように
最近、GuardでTitanium+CoffeeScriptの開発を快適に – ひげろぐを参考にGuard::CoffeeScriptを入れ、coffeescriptでflymakeする - Kentaro Kuribayashi's blogのFlymakeの設定を入れてCoffeeScriptでTitaniumでのアプリ作成を試してます。
Guard::CoffeeScriptでファイル変更の監視・コンパイルをしてると
ERROR: Guard::CoffeeScript failed to achieve its <run_on_change>, exception was: Errno::ENOENT: No such file or directory - coffee/app_flymake.coffee
というエラーが発生して頻繁にGuardが落ちる。
Flymake用の一時ファイルをGuardが検知し、コンパイルしようとするが、すぐに削除されるのでエラーになっているようだ。
そのまま参考になるruby-modeのflymakeでguardが誤動作しないようにする - むしゃくしゃしてやったを参考に、 flymake-create-temp-inplace を flymake-create-temp-with-folder-structure に変更し、同一ディレクトリ内でなく、tempディレクトリにflymake用のファイルを作成するようにした。
以下、diff
(setq flymake-coffeescript-err-line-patterns '(("\\(Error: In \\([^,]+\\), .+ on line \\([0-9]+\\).*\\)" 2 3 nil 1))) (defconst flymake-allowed-coffeescript-file-name-masks '(("\\.coffee$" flymake-coffeescript-init))) (defun flymake-coffeescript-init () (let* ((temp-file (flymake-init-create-temp-buffer-copy + 'flymake-create-temp-with-folder-structure)) - 'flymake-create-temp-inplace)) (local-file (file-relative-name temp-file (file-name-directory buffer-file-name)))) (list "coffee" (list local-file)))) (defun flymake-coffeescript-load () (interactive) (defadvice flymake-post-syntax-check (before flymake-force-check-was-interrupted) (setq flymake-check-was-interrupted t)) (ad-activate 'flymake-post-syntax-check) (setq flymake-allowed-file-name-masks (append flymake-allowed-file-name-masks flymake-allowed-coffeescript-file-name-masks)) (setq flymake-err-line-patterns flymake-coffeescript-err-line-patterns) (flymake-mode t)) (add-hook 'coffee-mode-hook 'flymake-coffeescript-load)
メモ
例えば/Users/foo/local/app/titanium/example/coffee/app.coffeeでエラーがある場合、エラー表示は以下のようになる。
[3] Error: In ../../../../../../../private/var/folders/OQ/OQ*************************/-Tmp-/Users/foo/local/app/titanium/example/coffee/app.coffee, Parse error on line 3: Unexpected 'TERMINATOR'
この/private/var/folders/..というのは何だろう?と見てみると、自分の環境では環境変数TMPDIR, TMP, TEMPを見ているようでmacではこうなってるみたい。
;; flymake.el -> flymake-create-temp-with-folder-structure (defun flymake-create-temp-with-folder-structure (file-name prefix) (unless (stringp file-name) (error "Invalid file-name")) (let* ((dir (file-name-directory file-name)) ;; Not sure what this slash-pos is all about, but I guess it's just ;; trying to remove the leading / of absolute file names. (slash-pos (string-match "/" dir)) (temp-dir (expand-file-name (substring dir (1+ slash-pos)) (flymake-get-temp-dir)))) (file-truename (expand-file-name (file-name-nondirectory file-name) temp-dir)))) ;; flymake.el -> flymake-get-temp-dir (defalias 'flymake-get-temp-dir (if (fboundp 'temp-directory) 'temp-directory (lambda () temporary-file-directory))) ;; files.el -> temporary-file-directory (defcustom temporary-file-directory (file-name-as-directory (cond ((memq system-type '(ms-dos windows-nt)) (or (getenv "TEMP") (getenv "TMPDIR") (getenv "TMP") "c:/temp")) (t (or (getenv "TMPDIR") (getenv "TMP") (getenv "TEMP") "/tmp")))) "The directory for writing temporary files." :group 'files :initialize 'custom-initialize-delay :type 'directory)
Emacsのphp-modeでalignする その2
4年越しですが Emacsのphp-modeでalignする - てつじんにっき で書いていたものの挙動が気になったので修正し、requireして使うよう改良してphp-align.elという名称でgithubにあげてみました。
GitHub - tetsujin/emacs-php-align: Emacs's alignment configuration for PHP.
話が変わりますが最近php-modeは https://github.com/rradonic/php-mode を使っています。オリジナルに比べて色々と機能追加やバグ修正されているようです。*1
と、していたら https://github.com/ejmr/php-mode の方がEmacs同梱になるというのをブコメで知りました。
b:id:tomoya emacs, php
PHP5.4 対応版。php-mode-version-number は 1.5.1 になってる。と思ったら、あっという間に 1.6 へ。そして Emacs 同梱へ http://comments.gmane.org/gmane.emacs.devel/142507 2011/07/26
こちらも5.4対応など色々と機能追加されているようです。
乗り換えようかな。
*1:5.4のtraitも簡単なパッチを送ったらマージしてくれました
anything-project.elでSymfony2
anything-project.elにSymfony2用のプロジェクトを定義してみた。
(ap:add-project :name 'symfony2 :look-for 'ap:symfony2-root-detector :grep-extensions '("\\.php" "\\.twig" "\\.yml") :exclude-regexp '("/cache/" "/logs/")) (defun ap:symfony2-root-detector (files) (ap:all-files-exist '("app" "bin" "src" "vendor" "web") files))
使ってみて、M-x anything-project-grepした時にcacheディレクトリなどが対象になって少し困ったので、ap:add-project-exという関数の追加とap:build-grep-commandを上書きして:grep-ignore-dirsというキーワード引数で除外するディレクトリを定義できるようにした。*1
最終的に以下のような感じ。
(require 'anything-project) (defun* ap:add-project-ex (&key name look-for (include-regexp ".*") (exclude-regexp nil) (exclude-directory-regexp nil) (grep-extensions nil) (grep-ignore-dirs nil)) (ap:add-project :name name :look-for look-for :include-regexp include-regexp :exclude-regexp exclude-regexp :exclude-directory-regexp exclude-directory-regexp :grep-extensions grep-extensions) (nconc (cdr (assq name ap:projects)) (list (cons :grep-ignore-dirs grep-ignore-dirs)))) (defun ap:get-grep-extra-options (key) (let ((grep-ignore-dirs (ap:get-project-data key :grep-ignore-dirs))) (mapconcat 'identity (mapcar (lambda (dir) (concat "--ignore-dir=" dir)) grep-ignore-dirs) " "))) (defun ap:build-grep-command (key) (let ((grep-extensions (ap:get-grep-extensions key)) (ack-command (ap:get-ack-command)) (grep-extra-options (ap:get-grep-extra-options key)) (xargs-command (ap:get-xargs-command)) (egrep-command (ap:get-egrep-command))) (concat ack-command " -afG " grep-extensions " " grep-extra-options " | " xargs-command " " egrep-command " -Hin " "%s"))) ;; PHP Symfony2 (ap:add-project-ex :name 'symfony2 :look-for 'ap:symfony2-root-detector :grep-extensions '("\\.php" "\\.twig" "\\.yml") :grep-ignore-dirs '("cache" "logs") ; これを新たに追加 :exclude-regexp '("/cache/" "/logs/")) (defun ap:symfony2-root-detector (files) (ap:all-files-exist '("app" "bin" "src" "vendor" "web") files))
そしてsymfony2.elが欲しくなってきた。
*1:emacs lisp知識不足でdefunしたけどdefadviceで出来るものなのかな?
PHPer.jpでSymfony2のblogチュートリアルを動かすためにやったこと
http://phper.jp/でhttp://docs.symfony.gr.jp/sf2-blog-tutorial/を動してみたので、雑なまとめですが、やった設定の備忘録。
最終的に下記のような構成に。
/path/to/approot/ - .phper/ # phper.jp用設定ファイル群 - Symfony/ # blogチュートリアルアプリケーション - phpMyAdmin/ # 管理用phpMyAdmin
1. phper.jpに登録
http://phper.jp/quickstartを見てユーザ登録〜デプロイまで進める。
ドキュメントルートは Symfony/web にしておく。
2. phperコマンドのインストール
Ruby製のphperというCLIツールが用意されているので、http://blog.phper.jp/2011/03/07/94/を参考にRuby環境整えて
$ gem install phper
で、phperコマンドをインストール。
4. phper.jp用に設定の修正
■ Symfony/web/app_dev.php、Symfony/web/config.phpのアクセス制限変更
http://blog.phper.jp/2011/03/05/28/の構成を見るとフロントにproxyが居るので、
$_SERVER['REMOTE_ADDR']だけじゃなくて、$_SERVER['HTTP_X_FORWARDED_FOR']も見てやるようにする。
# /path/to/approot/Symfony/web/app_dev.php # /path/to/approot/Symfony/web/config.php <?php ... $remoteIpAddresses = array_merge( (array)@$_SERVER['REMOTE_ADDR'], array_map('trim', explode(',', @$_SERVER['HTTP_X_FORWARDED_FOR'])) ); $allowIpAddresses = array( '127.0.0.1', '::1', 'your-global-ip-address', // 自分の接続元グローバルIP ); if (count(array_intersect($remoteIpAddresses, $allowIpAddresses)) === 0) { header('HTTP/1.0 403 Forbidden'); die('You are not allowed to access this file. Check '.basename(__FILE__).' for more information.'); } ...
■ .gitignore
下記をgitの管理対象外にする。
# /path/to/approot/Symfony/.gitignore */logs/* */cache/* app/config/parameters.ini
parameters.iniはローカル、phper.jp上で異なるのでgit管理対象外として、下記のようにした。
# /path/to/approot/Symfony/app/config/parameters.phper.ini [parameters] database_driver = pdo_mysql database_host = db.phper.jp database_name = yourname_appname database_user = yourname database_password = password
mysqlの接続情報などは、phper infoで確認できる。
$ phper info yourname-appname --> gitosis@git.phper.jp:yourname/appname.git --> mysql://yourname:password@db.phper.jp/yourname_appname
5. phpMyAdminを用意する
MySQLのコマンドラインクライアントが使えないのでphpMyAdminを入れる。
http://www.phpmyadmin.net/home_page/downloads.phpからDownloadして/path/to/approot/phpMyAdminに設置する。
また、できればphpMyAdminにはhttpsでアクセスしたい。
http://blog.phper.jp/2011/03/05/28/を見ると、phper.jpではエッジレイヤのNginxがSSLアクセラレーションするようなので、
SSL-Proxy下でphpMyAdminをSSLで動かす設定も入れておいた。
config.sample.inc.phpをコピーしてconfig.inc.phpを作成。下記を設定。
# /path/to/approot/phpMyAdmin/config.inc.php <?php .. // 書き換え $cfg['Servers'][$i]['host'] = 'db.phper.jp'; // SSL-Proxy下でSSLで動かす設定。 http://wiki.phpmyadmin.net/pma/Config#PmaAbsoluteUri $cfg['PmaAbsoluteUri'] = 'https://yourappname.phper.jp/pma/';
6. phper設定
プロジェクト直下の.phper/ディレクトリにいろいろとスクリプトや設定を記述できる。
詳しくはマニュアル参照。http://phper.jp/manual
■ deployの設定: /path/to/approot/.phper/deploy
#!/bin/sh ROOT_DIR=$(cd $(dirname $0)/..; pwd) # キャッシュの削除 rm -rf $ROOT_DIR/Symfony/app/cache/* # parameters.iniのシンボリックリンク作成 ln -sf $ROOT_DIR/Symfony/app/config/parameters.phper.ini $ROOT_DIR/Symfony/app/config/parameters.ini
■ アプリケーションサーバのApacheの設定: /path/to/approot/.phper/httpd.conf
/pmaでphpMyAdminにアクセス。後、Digest認証も一応かけておく。
Alias /pma "/var/www/sites/yourname-appname/phpMyAdmin" <Location pma> AuthType Digest AuthName "Secret Zone" AuthDigestDomain pma AuthDigestProvider file AuthUserFile "/var/www/sites/yourname-appname/.phper/.htdigest" Require valid-user </Location>
$ htdigest -c /path/to/approot/.phper/.htdigest "Secret Zone" user New password: **** Re-type new password: ****
7. デプロイする
$ git push
8. phper.jp上のデータベースにschema定義を流しこむ
blogチュートリアルのschema定義をdumpして、phpMyAdminから流しこむ。
$ app/console doctrine:schema:create --dump-sql ATTENTION: This operation should not be executed in an production enviroment. CREATE TABLE Post (id INT AUTO_INCREMENT NOT NULL, title VARCHAR(255) NOT NULL, body LONGTEXT NOT NULL, createdAt DATETIME NOT NULL, updatedAt DATETIME NOT NULL, PRIMARY KEY(id)) ENGINE = InnoDB
後はブラウザから動作確認して動くか確認。
実際はこんなにすんなり行かずに、トライアンドエラーを繰り返しました。PaaS楽しい!!
*1:gitに登録するのも嫌だが他の方法が思いつかなかったので
symfony1.4+Doctrineでbuild-formsで生成されるコードに独自WidgetやValidatorをセットする
開発が進むに連れてフォームで
- 表示するエラーメッセージを全体的に変更したい
- 全角英数を半角英数に変換したい
- その他色々と機能を拡張したい
などと、全体的な挙動を変えたいことが多くて、
$ symfony doctrine:build-forms
で生成される、lib/form/doctrine/base/Base***Form.class.php 内のWidget, Validatorを独自拡張したものに変更できないかなと思う事が多く、調べてみました。*1
$ symfony doctrine:build-forms --generator-class=myDoctrineFormGenerator
と、build-formsの--generator-classにクラス名を渡してGeneratorクラスでいろいろとやれば良いのですが、
オプション付けるのを忘れたり、他の人も触ることを考えるとデフォルト値を置き換えられないかなーと思って調べました。
既存taskを継承すれば良いかなと思い継承しましたが下記のようなエラーが。
$ cat lib/task/myDoctrineBuildFormsTask.class.php <?php class myDoctrineBuildFormsTask extends sfDoctrineBuildFormsTask { } $ symfony doctrine:build-forms PHP Fatal error: Uncaught exception 'sfCommandException' with message 'The task named "doctrine:build-forms" in "myDoctrineBuildFormsTask" task is already registered by the "sfDoctrineBuildFormsTask" task.' in /usr/local/apps/example/lib/vendor/symfony/lib/command/sfCommandApplication.class.php:142
...と、taskの正しい拡張方法がわかりませんでした。
前置きが長くなりましたが無理やりオプション書き換えてみました。BKっぽいですがメモ。
config/ProjectConfiguration.class.php
command.filter_optionsイベントを捕捉して、generator-classを置き換えます。
<?php require_once dirname(__FILE__).'/../lib/vendor/symfony/lib/autoload/sfCoreAutoload.class.php'; sfCoreAutoload::register(); class ProjectConfiguration extends sfProjectConfiguration { public function setup() { $this->dispatcher->connect( 'command.filter_options', array($this, 'commandFilterOptionsEventHook') ); } public function commandFilterOptionsEventHook(sfEvent $event, $options) { $task = $event->getSubject(); $parameters = $event->getParameters(); $commandManager = $parameters['command_manager']; if ($task->getFullName() === 'doctrine:build-forms') { $commandManager->getOptionSet() ->getOption('generator-class') ->setDefault('myDoctrineFormGenerator'); } return $options; } }
lib/generator/myDoctrineFormGenerator.class.php
- $extendedWidgetFormClasses
- $extendedValidatorClasses
のkey=>valueに置き換えたいクラスの定義を書きます。
<?php class myDoctrineFormGenerator extends sfDoctrineFormGenerator { protected $extendedWidgetFormClasses = array( 'sfWidgetFormDate' => 'myWidgetFormTextDate', // 日付のinput text入力 ); protected $extendedValidatorClasses = array( 'sfValidatorInteger' => 'myValidatorInteger', // 全角英数->半角英数 'sfValidatorDate' => 'myValidatorDate', // 全角英数->半角英数 ); public function getWidgetClassForColumn($column) { return $this->getExtendedClass( parent::getWidgetClassForColumn($column), $this->extendedWidgetFormClasses ); } public function getValidatorClassForColumn($column) { return $this->getExtendedClass( parent::getValidatorClassForColumn($column), $this->extendedValidatorClasses ); } protected function getExtendedClass($class, $extendedClasses) { return isset($extendedClasses[$class]) ? $extendedClasses[$class] : $class; } }
として、やるとsymfony doctrine:build-formsしてやると、生成されるソース内のWidget,Validatorが置き換わります。
Person: columns: name: string(255) date_of_birth: date allowance: integer(4)
public function setup() { $this->setWidgets(array( 'id' => new sfWidgetFormInputHidden(), 'name' => new sfWidgetFormInputText(), 'date_of_birth' => new myWidgetFormTextDate(), 'allowance' => new sfWidgetFormInputText(), )); $this->setValidators(array( 'id' => new sfValidatorChoice(array('choices' => array($this->getObject()->get('id')), 'empty_value' => $this->getObject()->get('id'), 'required' => false)), 'name' => new sfValidatorString(array('max_length' => 255, 'required' => false)), 'date_of_birth' => new myValidatorDate(array('required' => false)), 'allowance' => new myValidatorInteger(array('required' => false)), )); ...
ちなみに今回、例として置き換えてみたWidgetとValidatorは以下です。参考までに。
lib/widget/myWidgetFormTextDate.class.php
<?php class myWidgetFormTextDate extends sfWidgetFormDate { protected function renderDayWidget($name, $value, $options, $attributes) { $attributes['size'] = '4'; $attributes['maxlength'] = 2; return $this->renderWidgetFormInput($name, $value, $attributes); } protected function renderMonthWidget($name, $value, $options, $attributes) { $attributes['size'] = '4'; $attributes['maxlength'] = 2; return $this->renderWidgetFormInput($name, $value, $attributes); } protected function renderYearWidget($name, $value, $options, $attributes) { $attributes['size'] = '6'; $attributes['maxlength'] = 4; return $this->renderWidgetFormInput($name, $value, $attributes); } protected function renderWidgetFormInput($name, $value, $attributes) { $widget = new sfWidgetFormInput(array(), $attributes); return $widget->render($name, $value); } }
lib/validator/myValidatorDate.class.php
<?php class myValidatorDate extends sfValidatorDate { protected function doClean($value) { if (is_string($value)) { $value = $this->filter($value); } else if (is_array($value)) { $value = array_map(array($this, 'filter'), $value); } return parent::doClean($value); } protected function filter($value) { return mb_convert_kana($value, 'a'); } }
lib/validator/myValidatorInteger.class.php
<?php class myValidatorInteger extends sfValidatorInteger { protected function doClean($value) { if ($value !== null) { $value = mb_convert_kana($value, 'a'); } return parent::doClean($value); } }
*1:デフォルトでセットされるsfValidatorString,sfValidatorInteger等を差し替えたい
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にしてみたい。