🐓

LaravelでRSS/Atomフィードを配信・生成する

2022/07/15に公開

LaravelでRSSフィードを配信する手順。
需要が無いのかLaravelでの事例の日本語記事が無かったので紹介。

HowTo

PHPでRSSを配信するライブラリなどはありますが、それらは使わずにLaravelのMVCモデルを最大限活用します。

RSS2.0/Atomに準拠した仕様でViewファイル内でマークアップすることで記事リストとかでテンプレートエンジン(Blade)の@foreach等の便利な機能を使うことも出来ます。
(別にViewはHTMLでなくてはならないとは決められてないので)

実践

Controller

今回はRSSの表示が主題なのでDBから記事一覧を持ってきて~的な処理は割愛。事前に連想配列を用意しましょう。

RSSController.php
class RSSController extends Controller
{
    public function index()
    {
        $feed = [
            'info' => [
                'title' => 'オタク総研',
                'description' => 'アニメ/ゲーム/ガジェット/ITの新!総合メディア',
                'link' => 'http://0115765.com/',
                'language' => 'ja',
            ],
            'items' => [
                [
                    'title' => 'Nothing Phone (1) 8月発売決定&仕様公開=背面が光る7万円スマホ',
                    'description' => 'イギリス発のテクノロジーベンチャーNothing社が満を持して本日7月13日に初のスマートフォン「Nothing Phone(1)の全貌を発表した。異色を放つスケルトンボディの本製品は8月発売で価格は6.9万円~',
                    'link' => 'https://0115765.com/archives/9430    ',
                    'pubDate' => 'Tue, 12 Jul 2022 16:05:33 +0000',
                ],
		// ︙ 続く
            ],
        ];
        return response()->view('rss', compact('feed'))
	->header('Content-Type', 'application/xml; charset=UTF-8');
    }
}

ポイントはControllerでContent-Typeを指定すること。return view()で書かずに、return response()->view()形式で書くことで、Content-Typeを設定することが出来ます。(with等は使えません)

レスポンスのステータスやヘッダをコントロールしながらも、レスポンス内容としてビューを返す必要がある場合は、viewメソッドを使用してください。 https://readouble.com/laravel/9.x/ja/responses.html

Routing

次にルーティング。適当にどうぞ

web.php
Route::get('/feed', [\App\Http\Controllers\RSSController::class, 'index']);

View(Blade)

最後にXMLをマークアップします。

rss.blade.php
<?='<?xml version="1.0" encoding="UTF-8"?>' . PHP_EOL?>
<rss version="2.0">
    <channel>
        <title>{{ $feed['info']['title'] }}</title>
        <link>{{ $feed['info']['link'] }}</link>
        <description>{{ $feed['info']['description'] }}</description>
        <language>ja</language>
        <pubDate>{{ now() }}</pubDate>
    </channel>

    @foreach($feed['items'] as $item)
        <item>
            <title>{{ $item['title'] }}</title>
            <link>{{ $item['link'] }}</link>
            <description><![CDATA[{!! $item['description'] !!}]]></description>
            <pubDate>{{ $item['pubDate'] }}</pubDate>
        </item>
    @endforeach
</rss>

1行目はああやって書かないと↓になりますー

生HTMLをRSSで配信するには

特に記事全文をRSSに掲載する時、HTMLのタグが誤ってパースされるのを防ぐために<![CDATA[~~]]?で囲む必要があります。
また、BladeでControllerから渡った変数をマスタッシュ構文で表示させるとデフォルトではエスケープ処理をした状態になります。
エスケープせずに表示するには{{!! $hoge !!}}でOK。脆弱性には十分注意して下さい。
つまり今回は上記を2つ組み合わせて以下になります。

rss.blade.php
<description><![CDATA[{!! $item['description'] !!}]]></description>

一部省略している部分があるのでそちらはRSS 2.0仕様でチェックしてください
https://www.futomi.com/lecture/japanese/rss20.html

正常配信をチェック

あとはちゃんとRSSが配信されているかをチェックします。
Vivaldi等の高機能ブラウザは標準でリーダーがありますしChrome拡張機能とかでチェックするのも有り。

※以下英語記事を参考にしました

https://devdojo.com/bobbyiliev/how-to-add-a-simple-rss-feed-to-laravel-without-using-package

Discussion