先週のアップデートでsfZendPluginが更新しました。これを使って、symfonyでさくっとサイトのRSSフィードを一つにまとめて表示するアグリゲータを作成してみます。 今日は、もろもろの復習も兼ねてプロジェクトのセットアップから順序を追って説明します。
本アプリケーションは実用には不足しています。symfonyアプリケーション構築の参考にどうぞ。また、不具合等有りましたらコメント頂けると助かります。
プロジェクトのセットアップ
フィードアグリゲータのディレクトリのセットアップをします。
mkdir planet && cd planet
symfonyコマンドを利用して、planetプロジェクトを作成しfrontendアプリケーションの作成します。その後defaultモジュールを作成します。
symfony new planet symfony app frontend symfony module frontend homepage
ここからあまり一般的ではなくなるのですが、私製のプラグインsfSubversion、sfConfigure、sfLighttpdPluginをインストール`しています。必須では有りません。
dspluginsroot="http://svn.tracfort.jp/svn/dino-symfony/plugins" dsplugins="sfConfigurePlugin sfLighttpdPlugin" cd plugins svn co ${dspluginsroot}/sfSubversionPlugin for i in $dsplugins; do symfony svn-checkout-plugin ${dspluginsroot}/${i} done cd ..
symfonyの公式プラグインリポジトリからsfZendPluginプラグインをインストールします。
cd plungins svn export http://svn.symfony-project.com/plugins/sfZendPlugin cd ..
上のコードはsfSubversionPluginをインストールしていれば下のようにする事が可能です。
symfony svn-checkout-plugin http://svn.symfony-project.com/plugins/sfZendPlugin
上記でインストールしたプラグインを利用してプロジェクトのキックスタートコマンドを実行します。
symfony init-bootstrap symfony init-configure symfony init-lighttpd ./bootstrap ./configure --with-server-port=10191
config/databases.ymlのコメントを外します。
# fix-up sed -e 's/^#//' < config/databases.yml.in >config/databases.yml.in.tmp mv config/databases.yml.in.tmp config/databases.yml.in
フロントコントローラを開発環境用に作り直します。clear-controllerでは、prod環境のフロントコントローラは利用できないため注意して下さい。
rm web/index.php symfony init-controller frontend dev index
さっきインストールした私製プラグインを利用してブラウザを開きます。
symfony lighttpd open
以上で下記のような画面が開きます。

ここまで30秒程です。プロジェクト生成時にいつもこのプロセスを利用するため、シェルスクリプトにしています。
routingの修正
apps/frontend/config/routing.ymlを修正して@homepageを変更します。また、その他のデフォルトのroutingは不要ですので削除します。
homepage: url: / param: { module: homepage, action: index }
特に、:module/:actionのルーティングは便利な事も多いですが、意図しないリクエストが発生する危険性を鑑み削除する事にしています。
settingの修正
sfZendPluginを有効にするため下記の記述をapps/frontend/config/settings.yml行います。
all:
.settings:
escaping_strategy: both
escaping_method: ESC_ENTITIES
zend_lib_dir: %SF_ROOT_DIR%/plugins/sfZendPlugin/lib
autoloading_functions:
- [sfZendFrameworkBridge, autoload]以前のsfZendPluginとは使い方が変わっていますので注意して下さい。
Zend_Feedを試す
Zend_Feedを利用してフィードを取得します。apps/frontend/modules/homepage/actions/actions.class.phpに試しに以下のように記述します。
public function executeIndex() { $this->feeds = Zend_Feed::findFeeds('http://blog.symfony.jp/'); }
何も考えず、いきなりZend_Feedが利用できます。これってすごい事ですよね!
テンプレートapps/frontend/modules/homepage/templates/indexSuccess.phpは以下のようにします。
<?php foreach($feeds as $feed): ?> <h2><?php echo $feed->title() ?></h2> <ul> <?php foreach($feed as $entry): ?> <li> <h3><?php echo $entry->title() ?></h3> <div> <?php echo nl2br($entry->summary()); ?> </div> </li> <?php endforeach ?> </ul> <?php endforeach ?>
これを表示すると次のような画面がでます。一応表示されましたがなんかおかしいですね。http://blog.symfony.jpではATOMとRSSの両方のフィードを提供しているため二つ表示されてしまいます。また、RSSの本文が表示されていません。これらを解決します。
単一サイトのフィードを表示する
フィードの一番最初の物を持ってくるように修正します。
public function executeIndex() { $site_feeds = Zend_Feed::findFeeds("http://blog.symfony.jp/"); if(count($site_feeds) >= 1){ $feeds[] = $site_feeds[0]; } }
フィードの最初で本当に良いのか疑問ですが…
次に、単に以下のようにしていた部分を
<?php echo nl2br($entry->summary()); ?>
以下のように書き換えます。
<?php if($entry instanceof Zend_Feed_Entry_Rss){ echo nl2br($entry->description()); }else{ echo nl2br($entry->summary()); } ?>
これで単一フィードについてはそれらしく表示する事ができるようになりました。

アグリゲートをして表示する
次に複数のサイトからフィードを取得するように修正します。
$sites = array('http://blog.symfony.jp/','http://www.dino.co.jp'); foreach($sites as $site){ $site_feeds = Zend_Feed::findFeeds($site); if(count($site_feeds) >= 1){ $feeds[] = $site_feeds[0]; } } $this->feeds = $feeds;
このコードで複数のサイトからフィードを取得できました。
仕上げ
最後に、時系列にエントリーをソートします。これでexecuteIndex()は完成です。
public function executeIndex() { $entries = array(); $sites = array( 'http://www.symfony-project.com/', 'http://blog.symfony.jp/', 'http://www.dino.co.jp' ); foreach($sites as $site){ $site_feeds = Zend_Feed::findFeeds($site); if(count($site_feeds) >= 1){ foreach($site_feeds[0] as $entry){ $e["site"] =$site_feeds[0]->title(); $e["title"] =$entry->title(); $e["link"] =$entry->link(); if($entry instanceof Zend_Feed_Entry_Rss){ $e["body"] = strip_tags($entry->description()); $entries[strtotime($entry->pubDate())] = $e; }else{ $e["body"] = strip_tags($entry->summary()); $entries[strtotime($entry->published())] = $e; } } } } krsort($entries); $this->entries = $entries; }
indexSuccessの表示も最終的に以下のように変えました。
<ul> <?php foreach($entries as $time => $entry): ?> <li> <h2><?php echo link_to($entry["title"]."-". $entry["site"],$entry["link"]) ?></h2> <div> <p> <?php echo date("r",$time); ?> </p> <p> <?php echo nl2br($entry["body"]); ?> </p> </div> </li> <?php endforeach ?> </ul>
結果は、以下のようになりました。

とりあえず動きました、HTMLのタグを無条件に取ってしまっているあたりと、キャッシュを使っていないので毎回フィードの取得が実行されます。コーディング時間はここまでで30分程かかりました。 実用的にはまだまだですね。。。取りあえず今日はこんな所にしておきます。

