Caution: You are browsing the legacy symfony 1.x part of this website.

15日目: フィード

1.2 / Doctrine

昨日、あなた独自のsymfonyアプリケーションの開発を始めました。 やめないでください。symfonyをさらに学ぶので、アプリケーションに新しい機能を追加し、どこかでそれをホストし、コミュニティと共有しようと努めてください。

今日は完全に異なる題材に移ります。

求人情報を探している場合、新しいものが投稿されるとすぐに知らせが来ることが望ましいでしょう。 1時間ごとにWebサイトを確認するのはあまり便利ではないので、Jobeetのユーザーに最新情報を提供するために求人情報のフィードを追加します。

フォーマット

symfonyフレームワークはフォーマットとMIME-Typeをネイティブにサポートします。 これは同じModelとControllerがリクエストされたフォーマットに基づいて異なるテンプレートを持つことを意味します。 デフォルトのフォーマットはHTMLですが、txtjscssjsonxmlrdfもしくはatomのように、symfonyはいくつかのフォーマットをそのまま使えます。

フォーマットはリクエストオブジェクトのsetRequestFormat()メソッドを使って設定できます:

$request->setRequestFormat('xml');

しかしたいていの場合、フォーマットはURLに埋め込まれます。 このケースの場合、特別な変数sf_formatが対応するルートで使われる場合、symfonyは自動的に設定します。 求人リストに関して、リストのURLは次のとおりです:

http://jobeet.localhost/frontend_dev.php/job

このURLは次の内容と同等です:

http://jobeet.localhost/frontend_dev.php/job.html

両方のURLは同等です。 sfDoctrineRouteCollectionクラスによって生成されたルートは拡張子としてsf_formatを持ちhtmlはデフォルトのフォーマットだからです。 app:routesタスクを実行することであなた自身で確認できます:

Cli

フィード

最新のjobフィード

異なるテンプレートを作成することで異なるフォーマットを簡単にサポートできます。 最新の求人情報用にAtomフィードを作るには、indexSuccess.atom.phpテンプレートを作ります:

<!-- apps/frontend/modules/job/templates/indexSuccess.atom.php -->
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jobeet</title>
  <subtitle>Latest Jobs</subtitle>
  <link href="" rel="self"/>
  <link href=""/>
  <updated></updated>
  <author><name>Jobeet</name></author>
  <id>Unique Id</id>
 
  <entry>
    <title>Job title</title>
    <link href="" />
    <id>Unique id</id>
    <updated></updated>
    <summary>Job description</summary>
    <author><name>Company</name></author>
  </entry>
</feed>

sidebar

テンプレートの名前

htmlはWebアプリケーションでもっとも使われる共通のフォーマットなので、テンプレートの名前で省略できます。>indexSuccess.phpindexSuccess.html.phpテンプレートは同等でsymfonyは最初の方を使います。

なぜテンプレートのデフォルトのサフィックスはSuccessなのでしょうか? アクションはレンダリングするテンプレートを指し示す値を返すことができます。 アクションが何も返さない場合、次のコードと同等です:

return sfView::SUCCESS; // == 'Success'

サフィックスを変更したい場合、次のようなものを返すだけです:

return sfView::ERROR; // == 'Error'
 
return 'Foo';

以前見たように、テンプレートの名前はsetTemplate()メソッドを利用して変更することもできます:

$this->setTemplate('foo');

デフォルトでは、symfonyはフォーマットに従ってContent-Typeレスポンスを変更しHTMLではないすべてのフォーマットに関して、レイアウトは無効です。 Atomフィードに関して、symfonyはContent-Typeapplication/atom+xml; charset=utf-8に変更します。

Jobeetのフッターにおいて、フィードへのリンクを更新します:

<!-- apps/frontend/templates/layout.php -->
<li class="feed">
  <a href="<?php echo url_for('@job?sf_format=atom') ?>">Full feed</a>
</li>

内部URIは変数として追加されるsf_formatを伴うjobリストに対して同じです。

ブラウザーがフィードを自動的に検出できるように<link>タグをレイアウトのheadセクションに追加します:

<!-- apps/frontend/templates/layout.php -->
<link rel="alternate" type="application/atom+xml" title="Latest Jobs"
  href="<?php echo url_for('@job?sf_format=atom', true) ?>" />

hrefリンク属性に関して、url_for()ヘルパーの2番目の引数のおかげで絶対URLが使われます。

Atomテンプレートヘッダーを次のコードで置き換えます:

<!-- apps/frontend/modules/job/templates/indexSuccess.atom.php -->
<title>Jobeet</title>
<subtitle>Latest Jobs</subtitle>
<link href="<?php echo url_for('@job?sf_format=atom', true) ?>" rel="self"/>
<link href="<?php echo url_for('@homepage', true) ?>"/>
<updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', strtotime(Doctrine::getTable('JobeetJob')->getLatestPost()->getCreatedAt())) ?></updated>
<author>
  <name>Jobeet</name>
</author>
<id><?php echo sha1(url_for('@job?sf_format=atom', true)) ?></id>

created_atの日付をタイムスタンプとして取得するためにstrtotime()関数の使い方に注目してください。 最新投稿の日付を得るには、getLatestPost()メソッドを作ります:

// lib/model/doctrine/JobeetJobTable.class.php
class JobeetJobTable extends Doctrine_Table
{
  public function getLatestPost()
  {
    $q = Doctrine_Query::create()
      ->from('JobeetJob j');
    $this->addActiveJobsQuery($q);
 
    return $q->fetchOne();
  }
 
  // ...
}

フィードエントリは次のコードで生成できます:

<!-- apps/frontend/modules/job/templates/indexSuccess.atom.php -->
<?php use_helper('Text') ?>
<?php foreach ($categories as $category): ?>
  <?php foreach ($category->getActiveJobs(sfConfig::get('app_max_jobs_on_homepage')) as $job): ?>
    <entry>
      <title>
        <?php echo $job->getPosition() ?> (<?php echo $job->getLocation() ?>)
      </title>
      <link href="<?php echo url_for('job_show_user', $job, true) ?>" />
      <id><?php echo sha1($job->getId()) ?></id>
      <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', strtotime($job->getCreatedAt())) ?></updated>
      <summary type="xhtml">
       <div xmlns="http://www.w3.org/1999/xhtml">
         <?php if ($job->getLogo()): ?>
           <div>
             <a href="<?php echo $job->getUrl() ?>">
               <img src="http://<?php echo $sf_request->getHost().'/uploads/jobs/'.$job->getLogo() ?>"
                 alt="<?php echo $job->getCompany() ?> logo" />
             </a>
           </div>
         <?php endif; ?>
 
         <div>
           <?php echo simple_format_text($job->getDescription()) ?>
         </div>
 
         <h4>How to apply?</h4>
 
         <p><?php echo $job->getHowToApply() ?></p>
       </div>
      </summary>
      <author>
        <name><?php echo $job->getCompany() ?></name>
      </author>
    </entry>
  <?php endforeach; ?>
<?php endforeach; ?>

リクエストオブジェクト($sf_request)のgetHost()メソッドは現在のホストを返します。 企業ロゴの絶対パスを作る際に重宝します。

フィード

tip

フィードを作成する際に、curlもしくはwgetのようなコマンドラインツールを使えばフィードの実際の内容を見れるのでデバッグ作業が楽になります。

カテゴリの最新求人フィード

Jobeetのゴールの1つはよりターゲットを絞った求人情報を見つための手助けになることです。 ですので、それぞれのカテゴリ用のフィードを提供する必要があります。

最初に、異なるフォーマット用のサポートを追加するためにcategoryルートを更新しましょう:

// apps/frontend/config/routing.yml
category:
  url:     /category/:slug.:sf_format
  class:   sfDoctrineRoute
  param:   { module: category, action: show, sf_format: html }
  options: { model: JobeetCategory, type: object }
  requirements:
    sf_format: (?:html|atom)

これで、categoryルートはhtmlatomフォーマットの両方を理解します。 テンプレートのカテゴリフィードへのリンクを更新します:

<!-- apps/frontend/modules/job/templates/indexSuccess.php -->
<div class="feed">
  <a href="<?php echo url_for('category', array('sf_subject' => $category, 'sf_format' => 'atom')) ?>">Feed</a>
</div>
 
<!-- apps/frontend/modules/category/templates/showSuccess.php -->
<div class="feed">
  <a href="<?php echo url_for('category', array('sf_subject' => $category, 'sf_format' => 'atom')) ?>">Feed</a>
</div>

最後のステップはshowSuccess.atom.phpテンプレートを作ることです。 しかしこのフィードは求人の一覧も表示するので、_list.atom.phpパーシャルを作ることでフィードエントリを生成するコードをリファクタリングできます。 htmlフォーマットに関して、パーシャルはフォーマット専用です:

<!-- apps/frontend/job/templates/_list.atom.php -->
<?php use_helper('Text') ?>
 
<?php foreach ($jobs as $job): ?>
  <entry>
    <title><?php echo $job->getPosition() ?> (<?php echo $job->getLocation() ?>)</title>
    <link href="<?php echo url_for('job_show_user', $job, true) ?>" />
    <id><?php echo sha1($job->getId()) ?></id>
      <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', strtotime($job->getCreatedAt())) ?></updated>
    <summary type="xhtml">
     <div xmlns="http://www.w3.org/1999/xhtml">
       <?php if ($job->getLogo()): ?>
         <div>
           <a href="<?php echo $job->getUrl() ?>">
             <img src="http://<?php echo $sf_request->getHost().'/uploads/jobs/'.$job->getLogo() ?>"
               alt="<?php echo $job->getCompany() ?> logo" />
           </a>
         </div>
       <?php endif; ?>
 
       <div>
         <?php echo simple_format_text($job->getDescription()) ?>
       </div>
 
       <h4>How to apply?</h4>
 
       <p><?php echo $job->getHowToApply() ?></p>
     </div>
    </summary>
    <author>
      <name><?php echo $job->getCompany() ?></name>
    </author>
  </entry>
<?php endforeach; ?>

求人フィードのテンプレートを簡略化するために_list.atom.phpパーシャルを使うことができます:

<!-- apps/frontend/modules/job/templates/indexSuccess.atom.php -->
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jobeet</title>
  <subtitle>Latest Jobs</subtitle>
  <link href="<?php echo url_for('@job?sf_format=atom', true) ?>" rel="self"/>
  <link href="<?php echo url_for('@homepage', true) ?>"/>
  <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', strtotime(Doctrine::getTable('JobeetJob')->getLatestPost()->getCreatedAt())) ?></updated>
  <author>
    <name>Jobeet</name>
  </author>
  <id><?php echo sha1(url_for('@job?sf_format=atom', true)) ?></id>
 
<?php foreach ($categories as $category): ?>
  <?php include_partial('job/list', array('jobs' => $category->getActiveJobs(sfConfig::get('app_max_jobs_on_homepage')))) ?>
<?php endforeach; ?>
</feed>

最後に、showSuccess.atom.phpテンプレートを作ります:

<!-- apps/frontend/modules/category/templates/showSuccess.atom.php -->
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jobeet (<?php echo $category ?>)</title>
  <subtitle>Latest Jobs</subtitle>
  <link href="<?php echo url_for('category', array('sf_subject' => $category, 'sf_format' => 'atom'), true) ?>" rel="self" />
  <link href="<?php echo url_for('category', array('sf_subject' => $category), true) ?>" />
  <updated><?php echo gmstrftime('%Y-%m-%dT%H:%M:%SZ', strtotime($category->getLatestPost()->getCreatedAt())) ?></updated>
  <author>
    <name>Jobeet</name>
  </author>
  <id><?php echo sha1(url_for('category', array('sf_subject' => $category), true)) ?></id>
 
  <?php include_partial('job/list', array('jobs' => $pager->getResults())) ?>
</feed>

メインの求人情報フィードに関して、カテゴリ用の最新の求人情報の日付が必要です:

// lib/model/doctrine/JobeetCategory.class.php
class JobeetCategory extends BaseJobeetCategory
{
  public function getLatestPost()
  {
    $jobs = $this->getActiveJobs(1);
 
    return $jobs[0];
  }
 
  // ...
}

カテゴリフィード

また明日

symfonyの多くの機能と同じように、ネイティブのフォーマットサポートによって難なくフィードをWebサイトに追加できます。

今日は、求職者のユーザーエクスペリエンスを強化しました。 明日は、Webサービスを提供することで職の投稿者に優れた公開機能を提供する方法を見ることになります。