symfony 1.4でアラートメールを送信する

自宅サーバで動かしているsymfonyのタスクがエラーになっていたのを放置していただが、そもそもエラーになったらアラートメールを送信することを試みた。

先に書いておく必要があるんだけどここでの内容はsymfony 1.3,1.4のもので、今後のバージョンで動くかはわからんです。ぐぐってさまよって辿りついた人はうすうす感じているとは思うんだけどsymfony 1.0->1.1->1.2->1.3/1.4とバージョンの度にメール送信機能はこころころ変わっている。きっと2.0でも大きく変わるに違いない。

自分もsymfony 1.2->1.4でメール送れなくなってこまった一人である。

symfony 1.4でのメール送信は以下に記載されている。
http://www.symfony-project.org/more-with-symfony/1_4/ja/04-Emails

まずsymfony 1.2とかでは外部ライブラリとしてSwiftをつかってメール送信するのがいいよ。ということで実践していたんだけどsymfony 1.4ではSwiftがsymfonyに内部に取り込まれていて1.2のやり方でやるとバージョンが違うのかなんなのかバッティングするはそんなクラスねーとか怒られるはさんざんな目に会うので、symfony 1.4ではおとなしくsymfony’s Swiftでメールを送るのが良い。

symfony 1.4だとアクションクラスやタスククラスで$this->getMailer()すればSwiftでつくられたsfMailerが取れるようになっている。
http://www.symfony-project.org/more-with-symfony/1_4/ja/13-Leveraging-the-Power-of-the-Command-Line

というわけでシンプルに済ますと

public function execute($arguments = array(), $options = array())
{
 $mailer  = $this->getMailer();
 $mailer->composeAndSend($from, $recipient, $subject, $messageBody);
}

でメール送信できる。

しかしこれならphpの標準関数で十分なわけで、Swiftの恩恵を受けるべくメールに重要度をつけてみる。

  $mailer  = $this->getMailer();
  $message = Swift_Message::newInstance() 
               ->setFrom('from@example.com')
               ->setTo('to@example.com')
               ->setSubject('Subject')
               ->setBody('Body')
               ->setPriority(1)
          ;
   $mailer->send($message);

しかし実際やってみたらGMailでは重要度は参照されないの巻。

symfonyでlogやcacheを別フォルダに保存する

symfony 1.4.x用。

symfonyのdelopyコマンドはlogやcacheをexcludeで除外してくれるけど最初から別にすればよくね?ということでvarとかにおいてみるテスト。
config/ProjectConfiguration.class.phpを変更する。

class ProjectConfiguration extends sfProjectConfiguration
{
     public function setup()
     {
          $this->enablePlugins('sfDoctrinePlugin');
          $var = (PHP_OS == 'WINNT') ? 'C:\var' : '/var';
          $this->setCacheDir($var.DIRECTORY_SEPARATOR.'symfony'.DIRECTORY_SEPARATOR.'cache');
     $this->setLogDir($var.DIRECTORY_SEPARATOR.'log'.DIRECTORY_SEPARATOR.'symfony');
     }
}

symfonyのview.ymlネタ。というかyamlネタ。

view.ymlでjavascriptの[]の行が横長になってきたとき。[]なcollectionはyaml的には以下のようにも書ける。

 javascripts:
   - http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js
   - http://ajax.googleapis.com/ajax/libs/jqueryui/1.7.1/jquery-ui.min.js
   - jquery/jquery.cookie.js,jquery/thickbox.js
   - jquery/jquery.lightbox-0.5.pack.js

ここ参照のこと。
http://yaml.org/spec/1.0/#id2489726

symfonyでsfWidgetFormInputCheckboxの隣にlabelを出力する

自作のsfFormの派生クラスでsfWidgetFormInputCheckboxを使ってCheckboxの右隣にlabelを出力したかったんだけどよく分からない。

フィールドごとにrenderすれば当然できるけど、sfForm->render()だけで済ませたい。いろいろ調べたけど結局よくわらかないのでwidgetつくった・・・

class sfWidgetFormInputCheckboxWithLabel extends sfWidgetFormInputCheckbox
{
  public function __construct($options = array(), $attributes = array())
  {
    $this->addOption('with_label');
    parent::__construct($options, $attributes);
  }

  public function render($name, $value = null, $attributes = array(), $errors = array())
  {
    return parent::render($name, $value, $attributes, $errors)
    .$this->renderContentTag('label',$this->getOption('with_label'),array('for'=>$this->generateId($name, null)))
    ;
  }
}

あとはsfFormの中とかでwith_labelを指定して使う。

$this->setWidgets(array(
  'foo'=> new sfWidgetFormInputCheckboxWithLabel(array('with_label'=>'右に載せるラベルのテキスト')),
));

個別にwidgetのrenderの書式って拡張できないのだろうか?

symfonyとphp-twientでtwitterでログインするサイトをつくる

symfonyでつくったウェブサイトでtwitterを使ってログインするようにしてみた。自分でphp-twientというライブラリをつくっておいてもともとクライアント(bot)用だったのとOAuthも忘れてしまっていたので備忘録メモ。

復習。

まず、サイトをtwitterにアプリケーション登録する。http://twitter.com/oauth_clients
サイトなのでApplication TypeをBrowserにして、さらにログインに使うのでYes, use Twitter for login にチェックを入れる。

ここからsymfonyによる開発。今回のメモの実績はsymfony 1.4.2 (sandbox)。

twitterにアプリケーション登録して取得したキーを /apps/frontend/config/app.yml に書く

all:
  twitter:
    consumer_key: ’your twitter consumer key'
    consumer_secret: 'your twitter consumer securet'

次にphp-twientを取得して配置する
/path-to-sf_project/lib/vendor/php-twient

php-twientはautoload機能を持っているがとりあえずsymfonyにやらせる
apps/frontend/config/autoload.yml をつくって以下を書く

autoload:
  php-twient:
    name: php-twient
    path:        %SF_LIB_DIR%/vendor/php-twient/src
    recursive:   on

認証の処理を担当するモジュールを作成する、とりあえずoauthという名前にする。

$ php symfony generate:module frontend oauth

oauth/twitterというURLで認証実行、oauth/twitter_returnというURLでtwitterからのcallbackを処理する。すなわち /apps/frontend/modules/oauth/actions/actions.class.phpexecuteTwitter メソッド, executeTwitter_return メソッドをそれぞれ実装する。

処理の流れとしては

executeTwitter

  1. Twitter::oAuth() でサイトのConsumerキーを渡して Twitter_Auth_OAuth を取得
  2. Twitter_Auth_OAuth::getRequestToken にてcallbackを指定してリクエストを投げる
    • (アプリケーション登録したcallbackとは別のものも指定できるので開発時は重宝する)
  3. 成功するとユーザのauthTokenが手に入るので Twitter_Auth_OAuth::getAuthorizeUrl で認証URLを取得し飛ばす。コールバックに戻ってきたときに使うので一旦このtokenはセッションに入れとく
  4. twitterの認証URLでごにょごにょしてユーザが許可したらcallbackのURLに戻ってくる( executeTwitter_return が実行される)

executeTwitter_return

  1. Twitter::oAuthで今度はConsumerキーとセッションに入れてたtokenを使って Twitter_Auth_OAuth を取得
  2. twitterからもらった oauth_verifier をつかって Twitter_Auth_OAuth::getAccessToken を呼び出す
  3. twitter_idを取得できたのでこれをユーザのIDとしてログイン済みとする

コード

class oauthActions extends sfActions
{
     public function executeTwitter(sfWebRequest $request)
     {
          sfContext::getInstance()->getConfiguration()->loadHelpers(array('Url'));
          $twitter = new Twitter();
          $auth = $twitter->oAuth(sfConfig::get('app_twitter_consumer_key'),sfConfig::get('app_twitter_consumer_secret'));
          $token = $auth->getRequestToken(url_for('oauth/twitter_return',true));
          if ($token['oauth_callback_confirmed']) {
               $this->getUser()->setAttribute('oauth_token',$token);
               $this->redirect($auth->getAuthorizeUrl($token));
          }
          return sfView::NONE;
     }
     public function executeTwitter_return(sfWebRequest $request)
     {
          $oauth_token = $request->getParameter('oauth_token');
          $oauth_verifier = $request->getParameter('oauth_verifier');
          $token = $this->getUser()->getAttribute('oauth_token');
          if (!$token || $token['oauth_token'] != $oauth_token) {
               return sfView::ERROR;
          }
          $twitter = new Twitter();
          $auth = $twitter->oAuth(
               sfConfig::get('app_twitter_consumer_key'),
               sfConfig::get('app_twitter_consumer_secret'),
               $token['oauth_token'],
               $token['oauth_token_secret']);
          $token = $auth->getAccessToken($oauth_verifier);

          $this->getUser()->setAuthenticated(true);
          $this->getUser()->setAttribute('twitter_id', $token['user_id']);
          $this->getUser()->setAttribute('twitter_account', $token['screen_name']);
          return sfView::NONE;
     }
}

一応php_twientうごいた。ほっ。symfonyなんか知らんって人でも $request->getParameter$_REQUEST ,
$this->getUser()->getAttribute , $this->getUser()->setAttribute$_SESSION に脳内変換してもらえればだいたいわかるはず。

php_twientはエラーは例外吐くので適当にtry-catchしてエラーページ表示してください。

symfonyのsandbox用のvhost設定

ひょっとしたらsandboxに限らないのかもしれないけどsymfony 1.4の話。

ちなみにsymfony sandboxってのはsymfonyをインストールしなくても使えるsymfonyを同梱したsymfony projectのパッケージ。

さてsymfonyではデフォルトページで使う画像とかcssとかは/sf/に配置されるんだけど、sfフォルダがwebの下になかった。探してみたらdata/web/sfにあったので以下のようにする

<VirtualHost *:80>
  ServerNames f_sandbox
  Alias /sf /path/to/sf_sandbox/data/web/sf
  DocumentRoot "/path/to/sf_sandbox/web"
  DirectoryIndex index.php
  <Directory "/path/to/sf_sandbox/web">
    AllowOverride All
    Allow from All
  </Directory>
</VirtualHost>

Symfony 1.2にアップデート

自宅サーバのWebアプリで使っているPHPのフレームワークであるsymfonyのバージョンを1.2にあげました。

kwLabsのSandbox
x-jukebox.com
walkmanlog.com
などに影響があります。

下記を参考にやりました。

http://www.symfony-project.org/installation/1_2/upgrade
symfonyというか、Propelの1.2->1.3の変更でそこそこ影響をうけました。symfonyのModelをちゃんと使っていればPropelの変更などには影響をうけないんですが、複雑なSQLをModelでがんばるのが面倒で、中途半端にPropelを直接使っていたのでいくつか変更が必要でした。

PropelのUpdateにはこちらが参考になります。

http://propel.phpdb.org/trac/wiki/Users/Documentation/1.3/Upgrading
symfonyの変更でいうと、
Modelの日付型の属性を取得するときにformat=nullで指定してUnixTimeStampを取得するような感じにしていたのだけど、symfony 1.2ではformat=nullで日付型の属性を取得するとDateTimeオブジェクトが返ってくるようでした。素直にformat=’U’でTimeStampをとるように変更。はまったのはそれくらい。

kwLabsのSandboxをサブディレクトリでのSymfonyで運用してみる

最近、新規開発があんまりできておらずもっぱらサイトの内部的な整備に日々格闘しています。で、ようやく後回しになっていたkwLabsに着手。

砂場に30ページくらいあってちょっとデザインを変えると30ファイル直さないといけなかったりして、ある程度はviewのパーツ化はしていたのだけどそれでも結構面倒だったので思い切ってphpのSandboxはsymfonyで動かすことにした。

php以外の言語でも書いたりするので、symfonyはサブディレクトリで運用するとして/s/sandboxがPHP用の砂場となった。

AliasはってRewriteBaseすればいいんだろ。とタカをくくっていたが以外と苦戦したので手順を書いてみる。

/projectnameにおきたいとする

  1. symfonyプロジェクトをドキュメントルート(/path/to/docroot/)とは関係のないところで作る(/path/to/sfroot/)とする
  2. httpd.confを修正しAliasをはる
    • Alias /projectname /path/to/sfroot/web
  3. RewriteBaseをなおす
    • /path/to/sfroot/web/.htaccessを編集し下記を有効にする
      RewriteBase /projectname
  4. settings.ymlをなおす
    • /path/to/sfroot/apps/appname/config/settings.yml
      all:
      .settings:
      relative_url_root: /projectname
      web_debug_web_dir: /../sf/sf_web_debug
    • なぜかDebug用のcss/jsのリンクがおかしくなったのでweb_debug_web_dirを設定
  5. ドキュメントルート直下のcss/jsを参照するときは..を使って相対パスで指定する
    • (ドキュメントルートからの絶対パスの指定がわからない)
      stylesheets:    [/../css/main]

Symfony 1.1.5を使っているのだが、なぜかdebug用のcss/jsのリンクがおかしかったり、ドキュメントルート直下のcssの指定がわからなかったりして苦労した。いまだにview.ymlの細かい設定方法とかがわからない。cookbookじゃなくてちゃんとしたAPIドキュメントみたいのがほしい。

ちなみに、メンテナンス中になっているのはいつのまにかまともに動いていないページだったりする。今回は機械的な移植対応でおのおのを見直す余力はないのでしばらく放置する予定。

自宅サーバのsymfonyを1.1.4にアップデート

自宅サーバのsymfonyのバージョンを10/4にリリースされた1.1.4にアップデートしました。なんか(x-jukeboxとかWALKMAN.LOGとか)動かなくなってたらごめんなさい。

「symfonyで開発日記」が全然1.1系のバージョンアップの記事を書いてくれないので、購読をやめて普通に本家のブログを追っかけることにした。(というか最初からそれで良かったのでは・・・)
マイルストーンだと今月に1.2が出るっぽいんだけど、どうなんだろう。チケットまだ半分以上残ってるだけど。