add to hatena hatena.comment (0) add to del.icio.us (0) add to livedoor.clip (0) add to Yahoo!Bookmark (0) Total: 0

symfonyでの開発を楽にする3つの方法

symfonyの開発は慣れていても思い出すのは大変

symfonyで開発して多少慣れてくると色々と面倒だなぁと思う事があります。その1つが実装方法やサンプルコードを思い返すのに時間が掛かるということです。

今日は、この問題をちょこっと解決できる3つの方法

  • installerを利用する
  • skeletonを利用する
  • generatorを利用する

について紹介したいと思います。

下準備

今回紹介する方法はsymfonyが元々持っているスケルトンやジェネレーターを自前で用意するということです。
サンプルのコードはgithubに置きましたのでお好きなディレクトリにcloneなりしておきます。
ここでは、helpというディレクトリに展開しました。

% git clone git@github.com:brtriver/exSymfonyForBiginner.git ./help

そして、symfonyのプロジェクトは並列のディレクトリにsampleという名前で作成してあります。

|-- help
| |-- generator
| |-- installer
| |-- lib
| `-- skeleton
`-- sample
|-- apps
|-- cache
|-- config
|-- data
|-- lib
|-- log
|-- plugins
|-- test
`-- web

自前installerを利用する

sfFormExtraPluginはいつも使うのにインストールが面倒

ということがあると思います。もしくは、自社で利用するプラグインなどは毎回手作業でインストールするというのも大変です。

こういった場合は、generate:project実行時にinstallerを指定することができ、プロジェクト生成時にプラグインもインストールしてしまうことができるのです。

以下のようなinstaller.phpを用意し、

PHP:
  1. <?php
  2. $this->logSection('install', 'add plugin sfFormExtraPlugin');
  3. $this->getFilesystem()->execute('svn co http://svn.symfony-project.org/plugins/sfFormExtraPlugin/branches/1.3 plugins/sfFormExtraPlugin');
  4. $this->enablePlugin('sfFormExtraPlugin');


/.symfony generate:project sample --installer=../help/installer/installer.php

のように--installerオプションでファイルのパスを指定してあげるだけです。

Doctrineのスキーマの書き方なんて覚えられない

schema.ymlはカラムの定義からリレーションやビヘイビアなど多くの定義が書けます。でも、全部を完全に覚えている人は居ないでしょう。チートシートを見ながら、もしくは本家サイトのドキュメントを見ながら...という人がほとんどかと思います。

というわけで、本家ドキュメントにあるスキーマのサンプルコードが手元にあると便利ですよね?

これも、さきほどのinstaller.phpを利用してサンプル付きshema.ymlをコピーするようにします。

PHP:
  1. $this->logSection('install', 'add files for symfony beginners');
  2. $this->installDir(dirname(__FILE__).'/my_skeleton');

ファイルのコピー(インストール)はinstaller.phpを置いたディレクトリにあるmy_skeletonディレクトリから行われるように指定しています。

なので./my_skeleton/config/doctrine/schema.yml.sampleというファイルで以下のようなファイルを用意しておきます。

CODE:
  1. ---
  2. # http://www.doctrine-project.org/documentation/manual/1_2/en/yaml-schema-files
  3. # simple
  4. User:
  5.   columns:
  6.     username: string
  7.     password: string
  8.     contact_id: integer
  9.  
  10. Contact:
  11.   columns:
  12.     first_name: string
  13.     last_name: string
  14.     phone: string
  15.     email: string
  16.     address: string
  17.  
  18. # same above
  19. User:
  20.   columns:
  21.     username:
  22.       type: string(255)
  23.     password:
  24.       type: string(255)
  25.     contact_id:
  26.       type: integer
  27.   relations:
  28.     Contact:
  29.       class: Contact
  30.       local: contact_id
  31.       foreign: id
  32.       foreignAlias: User
  33.       foreignType: one
  34.       type: one
  35. .....(以下続く)

これで

/.symfony generate:project sample --installer=../help/installer/installer.php

を実行したときに、config/doctrineディレクトリにこのサンプルファイルもコピーされるので、あとはこのファイルを見ながらスキーマを作成すれば楽になります。

このように、自前installerを活用すれば

  • プラグインのインストール方法を思い出す
  • スキーマの書き方を思い出す

といった手間は多少なり軽減されます。

skeletonを利用する

日本語環境のための設定って何をするんだったっけ...

ということもよくあります。覚えてはいてもsettings.ymlに何かを書けばよかったんだけど...とかありますね。

そういった場合はskeletonの機能を利用します。これは、ひな形となるファイルを用意しておき、そのひな形ファイルでアプリケーションやモジュールの初期ファイルを生成する機能です。

generate:appのひな形ファイルを作る

まずは、help/skeleton/appのファイル一式をdata/skeleton/appにコピーします。

% mkdir data/skeleton
% cp -r ../help/skeleton/app ./data/skeleton/

ここでは標準のapp/config/settings.ymlに以下のコードを追加してあります。

CODE:
  1. # for Japanese
  2.     default_culture:        ja
  3.     i18n:                   true

この状態でいつもと同じgenerate:appを実行するだけです。


%./symfony generate:app frontend

作成されたアプリケーションのsettings.ymlにはさきほどのひな形に追加したコードが含まれるようになります。

コンポーネントを使いたいけどどう書くんだっけ...

これもよくあります。symfonyの標準のひな形にはactions.class.phpは含まれていますが、components.class.phpは含まれていません。
自分でファイルを用意しなくてはなりません。ファイルを書いたもののスペルミスで動かなかったりして悩んだりという苦い経験もあります。。

アクションやテンプレートで利用できるメソッドや書き方を思い出せない...

アクションでリファラーを取得するにはどうすればよかったっけ。。。テンプレートでエスケープさせずに出力する方法ってどうだったっけ。。。
色々とすぐに思い出せないことは多いです。

IEDを使っていたり、慣れてくればそれほど困らないことかもしれませんが、サンプルコードがアクションやテンプレートに付いていたら便利だと思いませんか?

generate:moduleのひな形ファイルを作る

これらを解決する方法もさきほどのアプリケーション生成と同じです。
helpディレクトリからmoduleのskeletonをコピーしておきます。


% mkdir data/skeleton
% cp -r ../help/skeleton/module ./data/skeleton/

あとは何も考えずにいつもどおりに


%./symfony generate:app frontend test

と実行するだけです。

すると、新しいひな形でファイル群が生成されます。

.
|-- actions
| |-- actions.class.php
| |-- actions.class.sample.php
| `-- components.class.php
|-- config
| `-- security.yml
`-- templates
|-- _sample.php
`-- indexSuccess.php

見てすぐわかるように、components.class.phpが自動生成されています。
さらには、configディレクトリも生成し、何も定義していないsecurity.ymlファイルも作成しています。
また、actions.class.sample.phpと_sample.phpはダミーのファイルです。
これらのファイルに以下のようなサンプルコードが書かれています。

* actions.class.sample.php

PHP:
  1. private function __sampleCode()
  2.   {
  3.     // for debug
  4.     $this->logMessage($message, 'debug'); // emerg, alert, crit, err, warning, notice, info, and debug
  5.  
  6.     // get Rouging object
  7.     $object = $this->getRoute()->getObject();
  8.    
  9.     // with form
  10.     $this->form = new HogeForm($object);
  11.     $this->form->bind($request->getParameter('hoge'));
  12.     if ($this->form->isValid()) {
  13.       // code here
  14.       $name = $this->form->getValue('name');
  15.     }
  16.     $widget = $this->form['name']->getWidget();
  17.    
  18.     // use Request Parameter
  19.     $name = $request->getParameter('name', 'default_value');
  20.     $all_parameters = $request->getParameterHolder()->getAll();
  21.     $condition = $request->isMethod('post'); // if request method is 'post', return true.
  22.     ....(続く)

*_sample.php

PHP:
  1. //---------------------------------//
  2. // HTML
  3. //---------------------------------//
  4. change layout to 'newLayout.php' in the global template dir.
  5. <?php decorate_with('newlayout')?>
  6.  
  7. add a new 'web/css/mystyle.css' style sheet file.
  8. <?php use_stylesheet('mystyle.css')?>
  9.  
  10. add a new 'web/js/myjs.js' javascript file.
  11. <?php use_stylesheet('myjs.css')?>
  12.  
  13. link tag
  14. <?php echo link_to('click here', '@routing_name?name=' . $name) ?>
  15. or
  16. <a href="<?php echo url_for('@routing_name') ?>">click here</a>
  17. open with a new window
  18. <?php echo link_to('click here', 'http://example.com', array('popup' => true)) ?>
  19.  
  20. image tag(web/images/title.gif)
  21. <?php echo image_tag('title.gif') ?>
  22. or
  23. <img src="<?php echo image_path('title.gif') ?>" />
  24. ...(続く)

これらを見ながらコードを書き進めていけば調べる量も減るかと思います。

このように、自前skeletonを活用すれば

  • settings.ymlなどにいつも書く内容を思い出さなくてよい
  • components.class.phpを標準で用意できる
  • サンプルコード付きのファイルを見ながら開発できる
  • $formの使い方を書いておける

といったメリットがあります。

generatorを利用する

フォームクラスの書き方がわかんない

慣れていても難しいのがsfFormです。その理由は多くのクラスやメソッドから構成されているので、調べないと使えないということにあります。
たとえば、テキストボックスをテキストエリアにしたい...なんてことも、widgetのクラスの指定を変えなければなりませんが覚えていないかぎり間違えてもおかしくありません。

ファーストロジックさんがsymfonyでのwidgetの書き方をまとめたテンプレートを紹介してくれています。そこで、このテンプレートが自動生成時に含まれていたら便利なのに...というわけでsymfonyのgenerator機能を使います。

myDoctrineFormGenerator

を用意する。
symfonyのdoctrine:buidl-formsタスクでは--generator-classというオプションを指定することができます。
ここでは、ただひな形として作成されるファイルにテンプレートコードを含ませたいだけなので、ジェネレータークラスで使用するテーマを自前のものに変更するだけで実現できます。

githubからダウンロードしたファイルからgeneratorディレクトリとmyDoctrineFormGenerator.class.phpファイルをコピーしておきます。


% cp -r ../help/generator ./data/
% cp -r ../help/lib/generator ./lib/

dataディレクトリは以下のようになっています。

data/generator
`-- sfDoctrineForm
`-- beginner
`-- template
|-- sfDoctrineFormBaseTemplate.php
|-- sfDoctrineFormGeneratedInheritanceTemplate.php
|-- sfDoctrineFormGeneratedTemplate.php
|-- sfDoctrineFormPluginTemplate.php
|-- sfDoctrineFormTemplate.php <= 変更してあるのはコレだけ
`-- sfDoctrinePluginFormTemplate.php

beginnerというのがテーマ名になります。また、テンプレートで標準ファイルから変更しているのはsfDoctrineFormTemplate.phpだけです。

また、myDoctrineFormGenerator.class.phpはもっと単純で以下の内容だけです。

PHP:
  1. <?php
  2. class myDoctrineFormGenerator extends sfDoctrineFormGenerator
  3. {
  4.   protected $theme = 'beginner';
  5. }

もともとのジェネレーターを拡張し、themeだけを変更しています。
これで、--generator-classオプションを指定してコマンドを実行します。

%./symfony doctrine:build-forms --generator-class=myDoctrineFormGenerator

ここでテンプレートが埋め込まれるファイルは
./lib/form/doctrine/XXXForm.class.phpが対象となります。
これらのファイルは対象となるモデル名に対応するフォームクラスファイルが存在しない場合のみ生成されます。
なので、すでにdoctrine:build-formsを実行している場合は新しいモデル定義が無い限り既存ファイルへ変更は行われません。

もし、既にフォームクラスファイルが存在する場合は

% mv lib/form/doctrine/XXXForm.class.php lib/form/doctrine/XXXForm.class.php.bkup

などのように名前を一時的に変更しておいて実行すると自前ジェネレータによって新しいフォームクラスファイルが作成されます。

これで、以下のようなフォームクラスが作成されます。

PHP:
  1. public function setup()
  2.   {
  3.     parent::setup();
  4.     // ここにwidgetの処理を記述します
  5.    
  6.     // ここにvalidationの処理を記述します
  7.     // $v =new sfValidatorCallback(array('callback' => array($this, 'checkUniqueEmail')));
  8.     // $v->setMessage('invalid', '既に登録されているメールアドレスです。パスワードを忘れた場合はパスワードの再発行を行ってください');
  9.   }
  10.   // saveする前に処理をしたい場合
  11.   public function updateObject($values=null)
  12.   {
  13.     $object = parent::updateObject($values);
  14.     /*
  15.      $object->setName("name: " . $object->getName());
  16.     */
  17.     return $object;
  18.   }
  19.   private function __samplecode()
  20.   {
  21.    
  22.     // widgetの記述サンプル
  23.     // http://symfony-jp.com/f/viewtopic.php?f=8&t=5&sid=0f84afcb9d94ba53468c6add2c4851e5
  24.     //ドロップダウンリストここから
  25.     $key = "sfWidgetFormChoice(dropDown)";
  26.     $this->widgetSchema   [$key] = new sfWidgetFormChoice(array('choices'=>array('key'=> 'value')));
  27.     $this->widgetSchema   [$key]->addOption('choices', array('なにも指定しないとドロップダウンに', 'なるよ', '!'));
  28.     $this->validatorSchema[$key] = new sfValidatorString();
  29.     $this->validatorSchema[$key] ->setOption('required', false);//入力必須ではない
  30.     //   $this->validatorSchema[$key] ->setOption('required', true);//入力必須
  31.     //   $this->validatorSchema[$key] ->setMessage('required', '入力必須です');
  32.     //   $this->widgetSchema   [$key] ->setHidden(true);  // フォームをhiddenにする場合
  33.     //ドロップダウンリストここまで
  34.  
  35.  
  36.     //リストボックスここから
  37.     $key = "sfWidgetFormChoice(list)";
  38.     $this->widgetSchema   [$key] = new sfWidgetFormChoice(array('choices'=>array('key'=> 'value')));
  39.     $this->widgetSchema   [$key]->addOption('multiple', true);
  40.     $this->widgetSchema   [$key]->setOption('choices', array('multipleを', 'Trueにすると', 'リスト', 'に', 'なる!', '・・・', 'よ'));
  41.     $this->validatorSchema[$key] = new sfValidatorString();
  42.     $this->validatorSchema[$key] ->setOption('required', false);//入力必須ではない
  43.     //   $this->validatorSchema[$key] ->setOption('required', true);//入力必須
  44.     //   $this->validatorSchema[$key] ->setMessage('required', '入力必須です');
  45.     //   $this->widgetSchema   [$key] ->setHidden(true);  // フォームをhiddenにする場合
  46.     //リストボックスここまで
  47.  
  48.  
  49.     //ラジオボタンここから
  50.     $key = "sfWidgetFormChoice(radio)";
  51.     $this->widgetSchema   [$key] = new sfWidgetFormChoice(array('choices'=>array('key'=> 'value')));
  52.     $this->widgetSchema   [$key]->addOption('expanded', true);
  53.     $this->widgetSchema   [$key]->setOption('choices', array('expandedを', 'Trueにすると', 'ラジオに', 'なる', 'よ!'));
  54.  
  55.     ...(続く)

このジェネレータは全フォームクラスに存在する必要はないと思いますので、どれか1フォームで仮に作成しておき、参照しながら作業すすめるのが便利かと思います。

このように、自前generatorを活用すれば

  • sfFormで調べる時間がぐっと減る
  • テーマを複数用意すれば使い分けできる

といったメリットがあります。

デメリット

これらのジェネレータにデメリットもあります。それは、コアのテンプレート側が何かの理由で更新されたときに自前側のファイルにも更新が必要になる場合があるということです。

ただし、symfony1.4系はもう大きな改修は入らないでしょうし、Symfony2ではこのまま使えることはまず無いと思われるので割り切って使えるのではないでしょうか。

まとめ

かなりの長文になってしまいましたが、これらの機能を使い自分自身、会社共通の便利なスケルトン、ジェネレーターを作成し共有すれば、楽して開発ができるようになると思います。

関連するその他の記事

Comments

Leave a Reply