16日目: ~メーラー~

昨日、リードオンリーの Web サービスを Jobeet に追加しました。アフィリエイトはアカウントを作ることができますが使えるようにする前に管理者によってアクティベイトされる必要があります。アフィリエイトがトークンを使えるようにするには、まだ ~email~ ノーテーションを実装する必要があります。これが今日作業することです。

symfony フレームワークは PHP の最高のメールソリューションの1つ: Swift Mailer を搭載しています。もちろん、ライブラリはデフォルトの機能の上に追加されたクールな機能を伴って symfony に完全に統合されています。

NOTE symfony 1.3/1.4 はバージョン4.1の ~Swift Mailer~ を使います。

シンプルなメールを送信する

アフィリエイトのアカウントが確認されアフィリエイトトークンを渡すときにアフィリエイトに通知するためのシンプルなメールを送信することから始めましょう。

activate アクションを次のコードに置き換えます:

[php]
// apps/backend/modules/affiliate/actions/actions.class.php
class affiliateActions extends autoAffiliateActions
{
  public function executeListActivate()
  {
    $affiliate = $this->getRoute()->getObject();
    $affiliate->activate();

    // アフィリエイトにメールを送信する
    $message = $this->getMailer()->compose(
      array('jobeet@example.com' => 'Jobeet Bot'),
      $affiliate->getEmail(),
      'Jobeet affiliate token',
      <<<EOF
Your Jobeet affiliate account has been activated.

Your token is {$affiliate->getToken()}.

The Jobeet Bot.
EOF
    );

    $this->getMailer()->send($message);

    $this->redirect('jobeet_affiliate');
  }

  // ...
}

NOTE コードをきちんと動くようにするために、メールアドレスを jobeet@example.com から実際のものに変更します。

symfony でのメール管理はメーラーオブジェクトに集約され、~getMailer()~ メソッドでアクションから取得できます。

~compose()~ メソッドは4つの引数を受け取り、メールメッセージオブジェクトを返します:

  • 送信者のメールアドレス (from);
  • 受信者のメールアドレス (to);
  • メッセージの件名;
  • メッセージのボディ;

メッセージの送信はメーラーインスタンスで send() メソッドを呼び出し引数としてメッセージを渡すだけです。ショートカットとして、~composeAndSend()~ メソッドを使うことでメールを作成し送信することを一度にできます。

TIP メールメッセージは Swift_Message クラスのインスタンスです。このオブジェクトの詳しい内容や、ファイル添付のような高度な方法を学ぶには Swift Mailer の公式ドキュメントを参照してください。

コンフィギュレーション

デフォルトでは、send() メソッドは受信者にメッセージを送信するためにローカルの SMTP サーバーを使おうとします。もちろん、symfony の多くのコンポーネントと同じように、これは全体に渡って設定可能です。

~ファクトリ~

以前の日の間に、すでにuserrequestresponse もしくは routing のような symfony のコアオブジェクトを話しました。これらのオブジェクトは symfony フレームワークによって自動的に作成され、設定され、管理されます。これらはつねに ~sfContext~ オブジェクトからアクセス可能で、フレームワークのほかのコンポーネントと同じで、設定ファイルの ~factories.yml~ を通して設定可能です。このファイルのコンフィギュレーションは環境によって設定できます。

sfContext がコアファクトリを初期化するとき、これはコンストラクタに渡すためのクラスの名前 (class) とパラメータ (param) のために factories.yml ファイルを読み込みます:

[yml]
response:
  class: sfWebResponse
  param:
    send_http_headers: false

上記のスニペットにおいて、レスポンスファクトリを作るために、symfony は sfWebResponse オブジェクトをインスタンス化し send_http_headers オプションをパラメータとして渡します。

SIDEBAR sfContext クラス

~sfContext~ オブジェクトはリクエスト、レスポンス、ユーザーなどの symfony のコアオブジェクトへの参照をもちます。sfContext は Singleton としてふるまうので、任意の場所から sfContext::getInstance() ステートメントを使えば symfony の任意のコアオブジェクトにアクセスできます:

[php]
$mailer = sfContext::getInstance()->getMailer();

1つのクラスで sfContext::getInstance() を使いたいと思うときは、これは~強結合~をもたらすので考え直してください。必要なオブジェクトを引数として渡すほうがずっとよいです。

~sfContext~ をレジストリとして使い set() メソッドを使い独自のオブジェクトを追加することもできます。これはオブジェクトを引数としてとり、get() メソッドはオブジェクトを名前として読み取るために後で使うことができます:

[php]
sfContext::getInstance()->set('job', $job);
$job = sfContext::getInstance()->get('job');

~デリバリ戦略~

ほかの多くの symfony のコアオブジェクトのように、メーラーはファクトリです。ですので、factories.yml 設定ファイルで行われます。デフォルトのコンフィギュレーションは次の内容を読み込みます:

[yml]
mailer:
  class: sfMailer
  param:
    logging:           %SF_LOGGING_ENABLED%
    charset:           %SF_CHARSET%
    delivery_strategy: realtime
    transport:
      class: Swift_SmtpTransport
      param:
        host:       localhost
        port:       25
        encryption: ~
        username:   ~
        password:   ~

新しいアプリケーションを作るとき、ローカルの factories.yml 設定ファイルはデフォルトのコンフィギュレーションを envtest 環境の実用的なデフォルトでオーバーライドします:

[yml]
test:
  mailer:
    param:
      delivery_strategy: none

dev:
  mailer:
    param:
      delivery_strategy: none

delivery_strategy 設定は symfony にメールを配信する方法を伝えます。デフォルトとして、symfony には4つの異なる戦略があります:

  • realtime: メッセージはリアルタイムでされる。
  • single_address: メッセージは単独のアドレスに送信される。
  • spool: メッセージはキューに保存される。
  • none: メッセージは無視される。

戦略が何であれ、メールはつねにロギングされ、Web デバッグツールバーの「mailer」パネルで利用可能です。

~メールトランスポート~

メーラーメッセージは実際にはトランスポートによって送信されます。トランスポートは factories.yml 設定ファイルで設定されデフォルトのコンフィギュレーションはローカルマシンの SMTP サーバーを使います:

[yml]
transport:
  class: Swift_SmtpTransport
  param:
    host:       localhost
    port:       25
    encryption: ~
    username:   ~
    password:   ~

Swift Mailer には3つの異なるトランスポートクラスが同梱されます:

  • ~Swift_SmtpTransport~: メッセージを送信するのに SMTP サーバーを使う。

  • ~Swift_SendmailTransport~: メッセージを送信するのに sendmail を使う。

  • ~Swift_MailTransport~: メッセージを送信するのに PHP ネイティブの mail() 関数を使う。

TIP Swift Mailer の公式ドキュメントの「トランスポートの種類」のセクションは組み込みのトランスポートクラスとこれらの異なるパラメータに関して知る必要のあるすべての内容を説明しています。

メールをテストする

symfony のメーラーでメールを送信する方法を見てきたので、正しく実装したことを保証する機能テストを書いてみましょう。デフォルトでは、メールの機能テストを楽にできるように symfony は mailer テスター (~sfMailerTester~) を登録します。

最初に、Web サーバーにロカールな SMTP サーバーがなければ、test 環境における mailer ファクトリのコンフィギュレーションを変更します。Swift_SmtpTransport クラスを Swift_MailTransport に置き換えなければなりません:

[yaml]
# apps/backend/config/factories.yml
test:

  # ...

  mailer:
    param:
      delivery_strategy: none
      transport:
        class:  Swift_MailTransport

それから、次の YAML 定義が収められている test/fixtures/administrators.yml ファイルを追加します:

[yaml]
sfGuardUser:
  admin:
    email_address: admin@example.com
    username: admin
    password: admin
    first_name: Fabien
    last_name: Potencier
    is_super_admin: true

最後に、バックエンドアプリケーションの affiliate 機能テストファイルのコードを次のものに置き換えます:

[php]
// test/functional/backend/affiliateActionsTest.php
include(dirname(__FILE__).'/../../bootstrap/functional.php');

$browser = new JobeetTestFunctional(new sfBrowser());
$browser->loadData();

$browser->
  info('1 - Authentication')->
  get('/affiliate')->
  click('Signin', array(
    'signin' => array('username' => 'admin', 'password' => 'admin'),
    array('_with_csrf' => true)
  ))->
  with('response')->isRedirected()->
  followRedirect()->

  info('2 - When validating an affiliate, an email must be sent with its token')->
  click('Activate', array(), array('position' => 1))->
  with('mailer')->begin()->
    checkHeader('Subject', '/Jobeet affiliate token/')->
    checkBody('/Your token is symfony/')->
  end()
;

以前のコードを動かすためには、昨日のフィクスチャファイル (data/fixtures/030_affiliates.yml) を test/fixtures/ ディレクトリにコピーします。それぞれの送信Eメールは ~checkHeader()~ と ~checkBody()~ メソッドのヘルプでテストできます。checkHeader()checkBody() の最初の引数は次のものの1つになります:

  • 完全にマッチするかチェックするための文字列;

  • これに対する値をチェックするための正規表現;

  • 値がマッチしないことをチェックするための否定形の正規表現 (! で始まる正規表現)。

NOTE デフォルトでは、チェックは最初のメール送信で行われます。いくつかのメールが送信されたら、~withMessage()~ メソッドでテストしたいものを選ぶことができます。withMessage() は最初の引数として受信者をとります。これは複数のメールアドレスが同じ受信者に送信される場合テストしたいメールアドレスを示す第2引数もとります。

-

TIP ほかの組み込みのテスターのように、debug() メソッドを呼び出すことで生のメッセージを見ることができます。

また明日

明日は、Jobeet のサイトに欠けている最後の機能である検索エンジンを実装します。

ORM