September 21, 2008

symfony1.1でswiftを使ってみた。

最近の開発は、ずっとsymfonyなので心中するしかないかなー、なんて思っていたりします。しかし、実際に使っていると、フレームワークではカバーしきれないコードを、ゴリゴリと、かっこ悪い方法で実装してしまったりして、嫌悪感いっぱいになってしまうことがあります。

その一つがメール周りです。symfony1.1からsfMailを使わない方向になりました(まぁ、使おうと思えば使えるけど)。そこで、代替とされているのが、Swift Mailerですね。直接SMTPが叩くので、速いだとか負荷が減るだとか、ほげほげだということで、swiftが採用されているようですが、個人的な感想を言えば、私はsfMailerで十分間に合っていたと思います。。。

とも言っていられないので、ちょっとswiftを使ってみました。でも、
How to send emails in symfony 1.1The symfony Cookbook How to send an emailを読んでもなんかしっくり来ないんですよね。理由は、これらのハウツーには、メールの本文をコントローラに書く方法しか提供していないからだと思います。確か前のsfMailerではViewテンプレートにメールの本文が書けたのですが、それってswiftでどうやったらいいのかなー、なんて思って調べてやってみました。まぁ、Jonathan Wageさんのアイデアなんだけどね。

というわけで、彼のアイデアをまとめると次のような感じか。
メールを使うアクションクラスをすべてsfSwiftMailerActionsの子クラスとする。
実際に呼び出す際には、親クラスの実装メソッドsendMailにモジュール名とアクション名を渡して、そのテンプレートに本文を書く。

  1. <?php
  2. class sfSwiftMailerActions extends sfActions
  3. {
  4.   public function preExecute()
  5.   {
  6.     $mailVars = sfSwiftMailerVars::getInstance()->getAll();
  7.  
  8.     foreach ($mailVars as $key => $value)
  9.     {
  10.       $this->$key = $value;
  11.     }
  12.   }
  13.  
  14.   public function sendEmail($module, $action)
  15.   {
  16.     sfConfig::set('symfony.view.' . $module . '_' . $action . '_layout', false);
  17.     $body = sfContext::getInstance()->getController()->getPresentationFor($module, $action);
  18.  
  19.     $mailVars = sfSwiftMailerVars::getInstance();
  20.  
  21.     $message = $mailVars->has('message')
  22.       ? $mailVars->get('message')
  23.       : new Swift_Message(null, null, 'text/html');
  24.     $swift = $mailVars->has('swift')
  25.       ? $mailVars->get('swift')
  26.       : new Swift(new Swift_Connection_Sendmail(Swift_Connection_Sendmail::AUTO_DETECT));
  27.  
  28.     $message->setSubject($mailVars->get('subject'));
  29.     $message->setBody($body);
  30.  
  31.     $swift->send($message, $mailVars->get('recipients'), $mailVars->get('from'));
  32.     $swift->disconnect();
  33.  
  34.     $mailVars->clear();
  35.   }
  36.  
  37.   public function __set($key, $value)
  38.   {
  39.     sfSwiftMailerVars::getInstance()->set($key, $value);
  40.  
  41.     return parent::__set($key, $value);
  42.   }
  43.  
  44. }
  45.  
  46. class sfSwiftMailerVars extends sfParameterHolder
  47. {
  48.   static $instance = null;
  49.  
  50.   public static function getInstance()
  51.   {
  52.     if (!self::$instance)
  53.     {
  54.       self::$instance = new sfSwiftMailerVars();
  55.     }
  56.  
  57.     return self::$instance;
  58.   }
  59.  
  60. }

ふむ。なるほど。テンプレートの内容を取るだけななら

  1. $body = sfContext::getInstance()->getController()->getPresentationFor($module, $action);

でできてしまうのですね。

私が手元で実装したものでは、もう少し劣化させて、上記とは違って、SwiftクラスやSwift_Messageクラスは直書きでやってしまっています。まぁ、デフォルトの挙動なんていらないと思うので。というわけで、このソースはテストはしていません。ほぼ同じものを手元で書いて、その動作は確認しているので、だいたいのロジックはこれでいいようです。

あとは、実際のアクションクラスにこんな感じで書けばいいのですね。

  1. class hogeActions extends sfSwiftMailerActions
  2. {
  3.   public function executeEdit($request)
  4.   {    $swift->send($message, $mailVars->get('recipients'), $mailVars->get('from'));
  5.     $this->recipients = 'example@ganchiku.com';
  6.     $this->from = 'example@ganchiku.com';
  7.     $this->subject = 'hello world';
  8.     $this->sendEmail('hoge', 'confirm');
  9.   }
  10.  
  11.   public function executeConfirm($request)
  12.   {
  13.   }
  14. }

で、あとは、confirmSuccess.phpに本文を適当に書けばいいのですね。ふーむ。確かに、すっきりはしますが、結構面倒ですね。。。もっといい方法があったら教えてください。

September 2, 2008

日系二世はドラマチックな人が多い。

ハワイに来ている目的は、日系人たちの昔の生活や文化などをなんかしらの方法で、デジタル化して残していこうということなんですね。すでに移民の一世は全滅しているのですが、二世はかろうじて生きています。だいたい80歳オーバーなので、おそらくあと10年経ったら、一気に減るだろうなー。

リリウオカラニ公園今、私がいるハワイ島のヒロというところは、この日系移民が多かった場所で、今でも多くの日系人が住んでいる。右の写真は、ヒロにあるリリウオカラニ公園の鳥居とマウナケアが写っているもの(ちなみ私はよくこの公園の周りをジョギングしている)。実際は、若い日系人は、すでに本土に移住してしまったりしていることが多いのだけども、おじいちゃんやおばあちゃんは日系人がいっぱいいる。そして、太平洋戦争のときに関係があったのが、二世もしくは、一世なんですね。一世は、日本人なので、そのまま強制就労所に入れられるわけだが、二世はすごく複雑な状況になったようだ。実際は、アメリカ側に付いて、最初はヨーロッパにアメリカ軍として参加することになるのだが、終戦間際には、日本にも行っている。ただ、この時期に日本に帰っている移民の家族もいたわけだ。そして、彼らは日本軍に付くことになる。また、戦後にマッカッサーと昭和天皇の間に入ってやりとりしていたのも、日系二世だったりするわけだ。日系二世は、日本語学校で教育を受けていたので、教育勅語を習っているわけ。そんな人たちなので、日本がなんとか残る方向で努力をしたようだ。

しかしながら、私たちの歴史教育では誰もこんなことを学ばないよね。誰も話したがらないし、私も知らなかった。ということで、戦争のみならずだけど、日系二世の文化や生活を残すという活動をしているわけ。
私とルース
右の写真に写っているのも私と日系二世のおばあちゃん。なんでも、ハワイでコンサートをしていた美空ひばりの伴奏をしていたとか。

私は歴史学者でもなんでもないのだけど、今生きている二世の話を聞く機会があるので、とても勉強になっている。

さて、件名に書いたように日系二世にはドラマチックな人生を歩んだ人が多い。もちろん、それは太平洋戦争が関係がある。最近、映画の主人公になれるんじゃないか、と思われる人生を歩んだ二人会ったので書いてみることにする。

一人は、日系二世として、ハワイで生まれたが、戦争前に帰国しており、その後、広島で育って、妹は原爆で死亡し、自分は神風特攻隊になったが、終戦したため、実際に行くことはなかった人。そして、彼は最後の日系移民として、またハワイに戻ることになった。日本軍になっていたため、アメリカの市民権は取り上げられていたし、今でもアメリカで語り継がれる神風の一員だったので、そんな人がアメリカに来て、いろいろ最初は大変だったに違いない。神風の一員で、その後、アメリカに移住したなんて彼くらいしかいないんじゃないかな。しかも、まだまだ元気。実は、アメリカに帰ってきてからもドラマがあるらしいのだけど、詳しくは聞いていない。その彼は自伝のような書籍を出すようだ。私の手元にもドラフトがある。

もう一人は、彼女も日系二世として、ハワイに生まれたが、同じく戦争前に帰国しており、英語と候文ができるため、戦争中は、英語の情報を候文に翻訳していた人。読む人は、東条英機だったと。東条英機は、候文しか書いたり、読んだりしなかったらしい。戦後は、逆にマッカーサーへの文書の翻訳をしていたそうだ。マジっすか。なんだか歴史の授業のようなんだけど。。。彼女の場合は、軍隊に入っていたわけではないので、その後、アメリカに戻ることができたようだ。彼女の話では、戦争中は牛乳はタダで手に入ったらしい。戦争中は、東京に住んでいたそうなのだが、大使館に牛乳を売る店があったようなのだけど、日本人は牛乳を飲まないので、店に行ってタダでもらっていたそうだ。今、聞くとマジっすか!といった内容がいろいろある。

まぁ、こんな感じですごく不思議なアメリカにいます。私の今まで知っていたアメリカとは大違いだ。朝のマクドナルドでは、日系二世のおじいちゃんやおばあちゃんがいっぱいコーヒーを飲みにくるらしい。シニアプライスで60セントだからね。なんか不思議な感じ。私も日系二世のおばあちゃんにマクドナルドおごってもらいました。マックチキンとサイドサラダとコーヒーとサンデーで$4を切るという不思議な値段。コーヒーはシニアプライスだから。

何人かは私にアメリカに移住すればいい、と言ってくれているけど、したくてもアメリカは受け入れてくれないですから!

しかし、ハワイに来て一度も泳いでいない。ダイビングも一度だけ。。。日本にいたときは、毎日泳いでいたのに。。。あと一ヶ月ハワイにいます。免許も取ろうかしら。ハワイは免許を持っていれば、ローカルプライスでいろいろ安くなるので。

August 9, 2008

よき人たれ

今年の夏は、エジプトでダイブマスターを取って、いつでもダイビングインストラクターで働けるようにしようと思っていたのですが、いろいろあって、ハワイのハワイ島に来ています。

多くの人と話していると、「いいなー、ハワイ。リラックスできて。」と言われますが、まったく持ってリラックスなんてできないです。なんつーか、ブルーワーカー状態です。そして、ボランティアに積極的に関わっています。そのために、来たわけではないので、本業もしっかりしないといけない。。。さて、実際に、ボランティアをしなかったら、引き篭もりになって、あまり人と話さないんだろうけど、おかげさまで、いろんな人に会う機会をいただいています。しかし、リラックスだけなら、京都にいたときの方がリラックスしていたと思います。

最近はいろいろ頼まれごとが多く、肉体労働も兼ねたお手伝いをしています。こうやってボランティアで社会に、コミュニティに、いいことをしていると、なぜかいろいろな人に助けてもらうことができることに気づきました。

昔は私もおせっかいで、いろいろ人を助けるのが好きだったのですが(美化しすぎ)、ここ8年ほどフリーライダーに近い状態になり、「自分さえ良ければ、いいや」と思うようになっていました。必死に長時間労働することが、「スマートではないし、自分は無理だなぁ」、なんて思っていたりしました。今でも、「雇われて働くことに関してのみ」、その思いは変わりません。雇用関係にあるのならば、仕事は仕事として、より効率良く、ビジネスライクに付き合うのがいいと思っています。基本的に、啓蒙本は生理的に受け付けなくて嫌いなのですが、最近の仕事啓蒙本なんかは、どんどんドラスティックに仕事をしていこう、という流れがあるように思えます。いや、普通の仕事に関しては、それで私も同意ですし、納得しています。私も、ホワイトカラーな仕事で自己実現なんて、ほとんどの人にはできない、と考えています。(昔、どこかの人事部長を引退をした人の講義を取って、その講義の中で、彼が仕事と自己実現を結びつけることを延々と語ることに、激しく嫌悪感を持ったことを思い出します。)

しかし、雇用関係ではなく、好意として働くこと(ボランティアとか)に関しては、自分の道徳に従い、「義理、人情、難波節」で働くことが、よい人生につながるように感じるようになりました。そして、よきことをしていると、いつの間にか、いろんな人が助けてくれる、ということをハワイで実感しています。

最近の風潮では、いろいろと他の人でもできることは、どんどんアウトーソーシングするのが、いいと言われているなか、このようなボランティアでは、どんどんDIYで解決していきます。そして、彼らが住んでいる社会にとって、コミュニティにとって、役に立つことがあれば、どんどんと助けにいきます。まぁ、何人かの人は、すでに引退した人だから時間を持て余している、とも思うのですが、この行動力はずば抜けています。

そして、彼らは、よき人のネットワークを持っています。よき人には、よき人を紹介して、さらに気持ちのいい活動をすることが出きるような社会を無自覚に作り上げています。アメリカでは、人のネットワークがとても大事だと言われています。会社に仕事を頼んでも、イマイチなものしか返ってこなかったり、全然反応がなかったりなんて日常茶飯事です。個人の裁量が大きいのですね。その代わり、素晴らしい人を知っていれば、それだけで取引コストと機会コストを最低まで落とすことができます。会社よりも、人なんですね。

最初は、表面的なお手伝いをしようと思っていた私も、こちらで、よきことをしている人を手伝うようになっています。そうすると、自分の気持ちも幸せになるばかりか、いろいろ私のために助けてくれるようにしてくれる人がおり、結果的にプラスの方向へ動いているような気がします。そこで、「よき人たれ」というのは、すごく重要なんだな、と気づきました。もちろん、「よきこと」とは、自分の中にある道徳に関してであって、100%皆が同じものを「よきこと」と思っているわけではないと思います。適当だけど、80%くらいかなぁ。

先日、ボランティアのつながりで、ヒロにあるライオンズクラブの人たちの晩ご飯と会合にお呼ばれしたのですが、なかなか興味深かったです。若者はいなくて、半分以上が引退した人たちばかりなのですが、善意で集まった昔の学級組織のような感じでした。いい年こいたオッサンらが、いい関係を築いていている学級組織です。変な表現ですが、懐かしくて、うれしかったです。というか、半分以上が日系人で、名字は、馴染み深いものばかりでした。三世までなると、日本語を話せる人があまりいないですね。

August 4, 2008

symfony1.1でsfGuardPluginを使う。パート2

ここ二日通っているベトナム料理のレストランの眼鏡っ子ウェイトレスがかわいくて、通おうかなーと思ってる私は、いろんな人に助けれながらハワイ島のヒロに滞在しています。ぜんぜん関係ないですが、今日は相撲の土俵の屋根の解体作業を手伝いました。きつい肉体労働で疲れました。。。

さて、パート1では、sfGuardPluginのsfGuardUserProfileとsfGuardUserを1対1のテーブル関係でリレーションがあった際に、アドミンジェネレータで一つのモデルをCRUDしているかのように扱う方法を説明しました。

しかし、実はこのアドミンジェネレータの方法は、symfony1.1的には、フォームの使い方が古いんですね。symfony1.1からは、sfFormクラスを使ってフォームを作るように、方法が変わったのです。なので、form_tagとかのヘルパー関数を使うのではなく、sfFormクラスの拡張クラスを使うことになります。また、Propelを使用している際には、うまく同期をとってくれるsfFormPropelの拡張クラスを使用することになりますね。例によって、ジェネレータで、ベースクラスと実際の処理を書く空クラスが作られ、それを上書きしていくという方法で実装するようになります。

さて、前提条件をここでおさらいするのは面倒なので、前回のパート1のまま、進めてみます。つまり、schema.ymlにsf_guard_user_profileを用意して、build-allをした状態とします。パート1では、backendとしましたが、今回は、frontendとしてみます。

今回のフォーム作成においては、CRUDのCreationのフォームとその値のデータベースへの登録についてを説明します。

パート1と同じように1対1の関連を持つ二つのテーブルを同時に登録する際の使い方について書きます。モジュール名はなんでもいいのですが、ベタにuserとしておきます。

  1. $ ./symfony project:init-module frontend user
  2. PHP Warning:  Xdebug MUST be loaded as a Zend extension in Unknown on line 0
  3. >> dir+      /home/shin/project/test/apps/frontend/modules/user/templates
  4. >> file+     /home/shin/project/test/apps/fr...user/templates/indexSuccess.php
  5. >> dir+      /home/shin/project/test/apps/frontend/modules/user/actions
  6. >> file+     /home/shin/project/test/apps/fr.../user/actions/actions.class.php
  7. >> file+     /home/shin/project/test/test/fu...al/frontend/userActionsTest.php
  8. >> tokens    /home/shin/project/test/test/fu...al/frontend/userActionsTest.php
  9. >> tokens    /home/shin/project/test/apps/fr...user/templates/indexSuccess.php
  10. >> tokens    /home/shin/project/test/apps/fr.../user/actions/actions.class.php

さて、環境が整いました。ここでフォームの入力可能なフィールドを何とするか決めます。パート1との続きということで、メールアドレス、パスワード、名前、生年月日とします。また、パート1と同じくsf_guard_userのusernameは、メールアドレスを入れる項目とします。パート1では、propel:build-allなどで、モデルを作成した際に、ついでにフォームクラスの雛形も生成されます。lib/form/sfGuardUserProfileForm.class.phpがすでにあると思います。そのクラスsfGuardUserProfileFomの空のメソッドconfigureに、フォームで使用するフィールドを選択したり、そのフィールドのラベルを変更したり、バリデターをつけたりしましょう。

つまり、大きく分けて、次の3つを行います。

  1. フォームで使用するフィールドを選択する
  2. ラベルを日本語化する
  3. バリデーションを追加する

フォームで使用するフィールドを選択する

さきほど、どのフィールドを入力可能とするか、決めました。メールアドレス、パスワード、名前、生年月日ですね。では、さっそく、それをセットしましょう。

  1. public function configure()
  2.     {
  3.         $years = range(date('Y') - 60, date('Y') - 17);
  4.         $this->setWidgets(array(
  5.             'username' => new sfWidgetFormInput(),
  6.             'name' => new sfWidgetFormInput(),
  7.             'password' => new sfWidgetFormInputPassword(),
  8.             'birthday' => new sfWidgetFormDate(array('format' => '%year%年%month%月%day%日', 'years' => array_combine($years, $years)))
  9.         ));
  10.     }

誕生日の年の項目は、17歳から60歳の人を対象としてみます。それより若い人、老いた人を対象にしたい場合は、適当に修正してください。

そして、作成したモジュールuserのactions.class.phpにexecuteRegisterメソッドを追加して、Viewファイル、registerSuccess.phpもuserモジュールのtemplates以下に追加しましょう。
actions.class.php

  1. public function executeRegister($request)
  2.     {
  3.         $this->form = new sfGuardUserProfileForm();
  4.     }

registerSuccess.php

  1. <?php $user = $form->getObject() ?>
  2. <form action="<?php echo url_for('user/register') ?>" method="post">
  3.   <table>
  4.     <tfoot>
  5.       <tr>
  6.         <td colspan="2">
  7.           <input type="submit" value="登録する" />
  8.         </td>
  9.       </tr>
  10.     </tfoot>
  11.     <tbody>
  12.       <?php echo $form ?>
  13.     </tbody>
  14.   </table>
  15. </form>

これで、簡単な登録フォームができました。デザイナーの方とフォームのデザインなどを協調的に作業する際には、この$formをこうやって単にechoするのではなく、詳細に書いていくことができるようですが、面倒ですので、ここではしません。

ラベルを日本語化する

さて、パート1のアドミンジェネレータのときと同じく、ラベルがまだ英語になっていますので、日本語に書き換えましょう。sfGuardUserProfileFormクラスのconfigureメソッドの先ほど書いた後あたりに、に次の行を加えて、ラベルをセットします。

  1. $this->widgetSchema->setLabels(array(
  2.             'username' => 'メールアドレス',
  3.             'password' => 'パスワード',
  4.             'name' => '名前',
  5.             'birthday' => '生年月日'
  6.          ));

バリデーションを追加する

これで、フォームができました。次は、バリデーションです。メールアドレスの項目は、必須で、メールアドレスのフォーマットチェック、ユニークチェックをしましょう。パスワードは、必須項目とするだけにします。nameも必須項目とするだけにします。実際に使用する際には、文字数チェックなども、そのアプリの仕様に基づいて変更してください。そして、ラベルをセットした後あたりに、次の行を加えて、バリデーションをセットします。

  1. $this->setValidators(array(
  2.             'username' => new sfValidatorEmail(
  3.                 array(),
  4.                 array('required' => 'メールアドレスの項目は必須です。',
  5.                       'invalid' => 'メールアドレスのフォーマットが間違っています
  6. 。もう一度ご確認ください。')),
  7.             'nickname' => new sfValidatorString(
  8.                 array(),
  9.                 array('required' => 'ニックネームの項目は必須です。')),
  10.             'password' => new sfValidatorString(
  11.                 array(),
  12.                 array('required' => 'パスワードの項目は必須です。')),
  13.             'birthday' => new sfValidatorDate(array('required' => false)),
  14.         ));
  15.         $this->validatorSchema->setPostValidator(new sfValidatorPropelUnique(
  16.             array('model' => 'sfGuardUser', 'column' => array('username')),
  17.             array('invalid' => '指定のメールアドレスは既に登録されています。')
  18.         ));
  19.  
  20.         $this->widgetSchema->setNameFormat('user[%s]');

最後のsetNameFormatは、パラメータのネームスペースのようなものです。フォームの値がuserというキーの連想配列に入るようにしています。つまり、user[password]とか、user[birthday][year]とかになって、サーバにパラメータが送られてくるようになります。

さて、あとは、actionクラスを修正して、ちゃんとバリデーションが通った際には、保存できるようにしましょう。さきほどは、formという変数をレンダーしているだけでしたので、ロジックを書きます。

  1. public function executeRegister($request)
  2.     {
  3.         $this->form = new sfGuardUserProfileForm();
  4.         if ($request->isMethod('post')) {
  5.             $this->form->bind($request->getParameter('user'));
  6.             if ($this->form->isValid()) {
  7.                 $this->form->setIsActive(false);
  8.                 $user = $this->form->save();
  9.                 $email = $user->getEmailaddress();
  10.                 // ここで確認メールを送って、is_activeをtrueにする操作をしたり。
  11.             }
  12.             // validation failed
  13.         }
  14.     }

ところで、

  1. $this-form->setActive(false);

とあるのですが、それは、まだ実装していませんね。なぜこんなことをするかというと、デフォルトのsfGuardUserのis_activeの値は、trueなのです。なので、セットせずに、保存してしまうとここで登録したユーザがそのままログインができるようになってしまうのですね。確認メールを送信するなどして、ちゃんと有効なメールアドレスを登録しているユーザだけを有効にしたい場合が多くあると思いますので、sfGuardUserProfileFormクラスにsetIsActiveメソッドを追加しておきましょう。確認メールの送り方などは、力尽きましたので、ここでは書きません。

というわけで、sfGuardUserProfileFormクラスは最終的に以下のようになりました。

  1. class sfGuardUserProfileForm extends BasesfGuardUserProfileForm
  2. {
  3.     public function configure()
  4.     {
  5.         $years = range(date('Y') - 60, date('Y') - 17);
  6.         $this->setWidgets(array(
  7.             'username' => new sfWidgetFormInput(),
  8.             'name' => new sfWidgetFormInput(),
  9.             'password' => new sfWidgetFormInputPassword(),
  10.             'birthday' => new sfWidgetFormDate(array('format' => '%year%年%month%月%day%日', 'years' => array_combine($years, $years)))
  11.         ));
  12.  
  13.         $this->widgetSchema->setLabels(array(
  14.             'username' => 'メールアドレス',
  15.             'password' => 'パスワード',
  16.             'name' => '名前',
  17.             'birthday' => '生年月日'
  18.         ));
  19.  
  20.         $this->setValidators(array(
  21.             'username' => new sfValidatorEmail(
  22.                 array(),
  23.                 array('required' => 'メールアドレスの項目は必須です。',
  24.                       'invalid' => 'メールアドレスのフォーマットが間違っています。もう一度ご確認ください。')),
  25.             'name' => new sfValidatorString(
  26.                 array(),
  27.                 array('required' => '名前の項目は必須です。')),
  28.             'password' => new sfValidatorString(
  29.                 array(),
  30.                 array('required' => 'パスワードの項目は必須です。')),
  31.             'birthday' => new sfValidatorDate(array('required' => false)),
  32.         ));
  33.  
  34.         $this->validatorSchema->setPostValidator(new sfValidatorPropelUnique(
  35.             array('model' => 'sfGuardUser', 'column' => array('username')),
  36.             array('invalid' => '指定のメールアドレスは既に登録されています。')
  37.         ));
  38.  
  39.         $this->widgetSchema->setNameFormat('user[%s]');
  40.     }
  41.  
  42.     public function setIsActive($value = false)
  43.     {
  44.         $this->object->setIsActive($value);
  45.     }
  46. }

もう一息です。toArrayとfromArrayを上書きする必要があるのを忘れていました。
sfGuardUserProfile.phpにtoArrayとfromArrayを上書きしてみます。

  1. public function fromArray($arr, $keyType = BasePeer::TYPE_PHPNAME)
  2.     {
  3.         parent::fromArray($arr, $keyType);
  4.         $this->setUsername($arr['username']);
  5.         $this->setPassword($arr['password']);
  6.     }
  7.  
  8.     public function toArray($keyType = BasePeer::TYPE_PHPNAME)
  9.     {
  10.         $result = parent::toArray($keyType);
  11.         $result['username'] = $this->getUsername();
  12.         $result['password'] = $this->getPassword();
  13.         return $result;
  14.     }


これで、とりあえず完了です。パート1で使用したアクセサやsaveメソッドは、そのまま使用することで、sf_guard_userとsf_guard_user_profileという二つのテーブルに保存することができるようになります。

ふー。疲れました。

間違いがありましたら、いろいろ教えてください。

July 28, 2008

symfony1.1でsfGuardPluginを使う。パート1

ならべて.comは、symfonyで開発された仕組みですが、1.0を使用しています。先日、ブログに貼り付けることができるウィジェットをリリースしており、もちろん開発は続けているのですが、現在その他に、別の仕組みを開発しており、そこでは、1.1を採用することにしました。symfony自体に関しては、もちろん根本にある使い方は変わっていないのですが、それなりに苦労しましたので、久しぶりにPHPネタでブログを書いてみます。

コードをできるだけ書かないことは、よりシンプルな開発となり、また、それがバグを減らすことなり重要ですよね。そこで、今回の開発では、アドミンジェネレータやプラグインを採用しようと決めました。プラグインでは、symfonyの開発者でもあるFabienさんのsfGuardPluginを使用しました。インストールできない人は、マニュアルを読んでください。

しかし、実際にsymfonyを使用して開発している方に話を聞いたのですが、実際のアプリとして作り込むには、sfGuardPluginは使いにくいので、アプリの作り方として勉強するならいいという話でした。今から書こうとする内容としては、いきなりこんなことを言うなんて凹んでしまいますが、そこは、よりコードを書かないようにゴリ押しで進めてみました。

さて、sfGuardPluginでは、ユーザの持つ情報をsf_guard_userテーブルに保存し、sfGuardUserというモデルで管理しています。このモデルのyamlスキーマは以下のようになっています。

  1. sf_guard_user:
  2.     _attributes:    { phpName: sfGuardUser }
  3.     id:             ~
  4.     username:       { type: varchar, size: 128, required: true, index: unique }
  5.     algorithm:      { type: varchar, size: 128, required: true, default: sha1 }
  6.     salt:           { type: varchar, size: 128, required: true }
  7.     password:       { type: varchar, size: 128, required: true }
  8.     created_at:     ~
  9.     last_login:     { type: timestamp }
  10.     is_active:      { type: boolean, required: true, default: 1 }
  11.     is_super_admin: { type: boolean, required: true, default: 0 }

sfGuardPluginに付いてくるsfGuardUserモジュールによって、sf_guard_userだけのCRUDは可能です。しかし、ユーザの属性には、もっといろいろな情報を持たせて、同時に登録したり、修正したいですよね?例えば、メールアドレスだったり、住所だったり、生年月日だったり、と。こういうときにあるのが、sf_guard_user_profileテーブルです。自分のスキーマファイルにsf_guard_user_profileを好きなカラムで指定することができます。そうすると、アクションクラスの中で$this->user->getProfile()と、持ってくることができます。つまり、テーブルが二つになって、1対1のリレーションで構成を作ってくれます。個人的にはこの1対1のリレーションが嫌いなのですが、プラグインの中を変更することは嫌ですので、このsf_guard_user_profileを使用しようと思います。しかし、やはり1対1のためか、アドミンジェネレータや、1.1から変更のあったフォーム周りを使用しようとすると結構大変でした。というわけで、ゴリ押しです。

さて、今開発しているものをそのまま持ってきてしまうと、説明がややこしくなったり、権利関係で問題になりそうですので、簡略化したモデルを使用しましょう。例えば、次のようなものです。

  1. sf_guard_user_profile:
  2.     _attributes: { phpName: sfGuardUserProfile }
  3.     id:
  4.     user_id:
  5.       type: integer
  6.       required: true
  7.       foreignTable: sf_guard_user
  8.       foreignReference: id
  9.       onDelete: cascade
  10.       onUpdate: cascade
  11.     name:
  12.       type: varchar(64)
  13.       required: true
  14.     birthday:
  15.       type: date
  16.     created_at:
  17.     updated_at:

準備は整いました。というわけで、ようやく本題。今回のsfGuardPluginネタは、二つのポストに分けて書こうと思います。前半のこのポストでは、symfony1.1のsfGuardPluginを、アドミンジェネレータを使用する方法について書きます。後半の次のポスト(予定)では、同様の環境をアドミンジェネレータではなく、symfony1.1から採用された新しいフォームクラスを使用する方法について書きます。

symfonyのアドミンジェネレータは非常に優れていますね。私は初めて使用したときは、単なるCRUDをやってくれるだけなのかな、とバカにしていたのですが、設定ファイルであるgenerator.ymlを編集したり、パーシャルを使用したりすることによって、とても柔軟に仕組みを作ることができるようになっています。しかも、コードをあまり書くことなくに、です。いやぁ、素晴らすぃ。

さて、今回の仕組みの目的は、sf_guard_userでは、格納できる情報が少ないので、sf_guard_user_profileを使用して格納できる情報を増やして、かつ、アドミンジェネレータで一つのモジュールで管理することです。

アドミンジェネレータでは、一つのモデルに対して一つのモジュールを管理する際には、とても有効に使うことができるのですが、複数のモデルを一つのモジュールで編集するには、苦労します。というか、そもそも、複数のモデルを一つのページで編集をさせるという設計がイマイチな感じがしますが、このsf_guard_user_profileを使用する以上は、しょうがないです。ということで、ゴリ押しです。

さて、プロジェクトやアプリケーションを作ったりするのは、ここでは説明しません。それらで躓いている人は、マニュアル