jpMailPluginをsymfony1.1以降で使う場合

symfony1.2でaskeet実践のための準備としてsymfonyでメール送信ネタが続きます。

すでに周知の事実としてjpMailPluginはそのままではsymfony1.1以降では動きません。
これは、sfMailクラスがバンドルされなくなったことが一番の原因なのですが、それ以外にも色々とハマります。

とりあえず現状分かっている範囲をまとめます。

symfony1.1で利用する場合

spanstyle::monologさんところが実際に使えるようになるまでを紹介してくれています。
ref: jpMailPluginをsymfony1.1.5に対応してみた(暫定)

getPresentationForの所属クラスが変更になっている

symfony1.0のころとの違いはgetPresentationForメソッドはコントローラークラスに移動しているので注意

$this->getController()->getPresentationFor(...)

getPresentationForで第3引数でビュークラスを指定してもsfPHPに上書きされてしまう

手元のsymfony1.2での現象なのですが(バグのような気もしますが)、jpMailと指定してもsfPHPViewクラスでレンダー処理されるという悲しい結果になってしまいます。
これは引数で渡した値よりも設定ファイル(module.yml)の方が強く効くためのようです。
なので、getPresentationForで利用する専用のモジュールを作成しmodule.ymlでview_classを指定しなければ意図した動作になりませんでした。

アプリケーション/modules/モジュール名/config/module.yml

all:
  view_class: jpMail
  is_internal: on

symfony1.2でjpMailViewクラスを使いたい

新規プロジェクトで使うのであればgetPresentationForを利用せずにgetComponentかgetPartialを使うのが自然かと思います。
もし、symfonyを1.0からアップグレードして使いたい場合は以下の手順でjpMailPluginを使うことができる(みたい)です

  1. symfony1.1以降で不足しているsfMail.class.phpをsymfony1.0からもってくる(アップグレードの場合はもともとあるので必要ないと思いますが。。)
  2. jpMailView.class.phpをsymfony1.2に対応した以下のクラスに置き換える

*1

<?php

/**
 *
 * @package    jpMail.plugin
 * @subpackage view
 * @author     brt river <brt.river@gmail.com>
 */
class jpMailView extends sfPHPView
{
  /**
   * Retrieves the template engine associated with this view.
   *
   * @return string sfMail
   */
  public function getEngine()
  {
    return 'jpMail';
  }

  /**
   * Configures template for this view.
   *
   * @throws <b>sfControllerException</b> If the configure fails
   */
  public function configure()
  {
    // view.yml configure
    parent::configure();

    // require our configuration
    $moduleName = $this->moduleName;
    
    if ($file = sfContext::getInstance()->getConfigCache()->checkConfig('modules' . sfConfig::get('sf_app_module_dir_name').'/'.$this->moduleName.'/config/mailer.yml',true)) {
      require(sfContext::getInstance()->getConfigCache()->checkConfig('modules' . sfConfig::get('sf_app_module_dir_name').'/'.$this->moduleName.'/config/mailer.yml'));
    }
  }

  /**
   * Renders the presentation and send the email to the client.
   *
   * @return mixed Raw data of the mail
   */
  public function render($templateVars = null)
  {

    $template         = $this->getDirectory().'/'.$this->getTemplate();
    $actionStackEntry = sfContext::getInstance()->getActionStack()->getLastEntry();
    $actionInstance   = $actionStackEntry->getActionInstance();

    $retval = null;

    // execute pre-render check
    $this->preRenderCheck();

    if (sfConfig::get('sf_logging_enabled'))
    {
      $this->dispatcher->notify(new sfEvent($this, 'application.log', array(sprintf('{sfView} render "%s"', $template))));
    }

    // get sfMail object from action
    $mail = $actionInstance->getVarHolder()->get('mail');
    if (!$mail)
    {
      $error = 'You must define a sfMail object named $mail ($this->mail) in your action to be able to use a sfMailView.';
      throw new sfControllerException($error);
    }

    // assigns some variables to the template
    //$this->attributeHolder->add($this->getGlobalVars());
    //$this->attributeHolder->add($actionInstance->getVarHolder()->getAll());

    // render main template
    $retval = $this->renderFile($template);
  

    // render main and alternate templates
    $all_template_dir  = dirname($template);
    $all_template_regex = preg_replace('/\\.php$/', '\..+\.php', basename($template));
    $all_templates = sfFinder::type('file')->name('/^'.$all_template_regex.'$/')->in($all_template_dir);
    $all_retvals = array();
    foreach ($all_templates as $templateFile)
    {
      if (preg_match('/\.([^.]+?)\.php$/', $templateFile, $matches))
      {
        $all_retvals[$matches[1]] = $this->renderFile($templateFile);
      }
    }

    // send email
    if (sfConfig::get('sf_logging_enabled'))
    {
      $this->dispatcher->notify(new sfEvent($this, 'application.log', array('{sfView} send email to client')));
    }

    // configuration prefix
    $config_prefix = 'sf_mailer_'.strtolower($this->moduleName).'_';

    $vars = array(
      'mailer',
      'priority', 'content_type', 'charset', 'encoding', 'wordwrap',
      'hostname', 'port', 'domain', 'username', 'password'
    );
    $defaultMail = new jpMail();
    foreach ($vars as $var)
    {
      $setter = 'set'.sfInflector::camelize($var);
      $getter = 'get'.sfInflector::camelize($var);
      $value  = $mail->$getter() !== $defaultMail->$getter() ? $mail->$getter() : sfConfig::get($config_prefix.strtolower($var));
      $mail->$setter($value);
    }
    $mail->setBody($retval);

    // alternate bodies
    $i = 0;
    foreach ($all_retvals as $type => $retval)
    {
      if ($type == 'altbody' && !$mail->getAltBody())
      {
        $mail->setAltBody($retval);
      }
      else
      {
        ++$i;
        $mail->addStringAttachment($retval, 'file'.$i, 'base64', 'text/'.$type);
      }
    }

    // preparing email to be sent
    $mail->prepare();

    // send e-mail
    if (sfConfig::get($config_prefix.'deliver'))
    {
      $mail->send();
    }
    return $mail->getRawHeader().$mail->getRawBody();
  }
}

とりあえず、エラーが出ていた箇所をsymfony1.2で動くように書き換えてみました。(例外処理の変更、ログの出力変更などなど)

小論

getPresentationForを使うのはなにかと面倒なことになりそう(引数でViewクラスを書き換えることができないため)。
getComponentかgetPartialを使うように実装した方が良いと思われる。

追記

getPresentationForを使わずに、もっとシンプルにPHPMailer, Swift Mailer, Qdmailで簡単に日本語メールを送るためのプラグイン(jpSimpleMailPlugin)を作成中です。
どんな感じになるかはjpSimpleMailPluginを見てもらえればわかるかと。

まだ公開していないのは公開場所をopenpearにしたいからその準備が週末でないとできないということなんですが。
でも、アンダーバー区切りのパッケージの中にいきなりキャメルケースのsymfonyプラグインを入れるのに抵抗を感じてるのですが。。

*1:テスト不足感があるので後日ちゃんと対応します。