Skip to content

Laravel

Rumen Damyanov edited this page Jul 29, 2025 · 1 revision

Laravel Examples

This guide shows how to use php-feed with Laravel applications.

Installation

Add the package to your Laravel project:

composer require rumenx/php-feed

No service provider registration is needed - the package works out of the box!

Basic Laravel Controller

Create a feed controller to handle your RSS/Atom endpoints:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Controller;
use Illuminate\Http\Response;
use Rumenx\Feed\FeedFactory;
use App\Models\Post;

class FeedController extends Controller
{
    public function posts(): Response
    {
        $feed = FeedFactory::create();
        $feed->setTitle(config('app.name') . ' - Latest Posts');
        $feed->setDescription('Latest blog posts from ' . config('app.name'));
        $feed->setLink(url('/'));
        $feed->setLanguage('en');

        // Get latest published posts
        $posts = Post::published()
            ->orderBy('created_at', 'desc')
            ->limit(20)
            ->get();

        foreach ($posts as $post) {
            $feed->addItem([
                'title' => $post->title,
                'author' => $post->author->name,
                'link' => route('posts.show', $post->slug),
                'pubdate' => $post->created_at,
                'description' => $post->excerpt,
                'category' => $post->categories->pluck('name')->toArray()
            ]);
        }

        return response($feed->render('rss'), 200, [
            'Content-Type' => 'application/rss+xml; charset=utf-8'
        ]);
    }
}

Routes

Add feed routes to your routes/web.php:

<?php

use App\Http\Controllers\FeedController;

Route::get('/feed', [FeedController::class, 'posts'])->name('feed.posts');
Route::get('/feed.rss', [FeedController::class, 'posts'])->name('feed.rss');
Route::get('/feed.xml', [FeedController::class, 'posts'])->name('feed.xml');

// Different content types
Route::get('/feed/posts', [FeedController::class, 'posts'])->name('feed.posts');
Route::get('/feed/news', [FeedController::class, 'news'])->name('feed.news');

Using Eloquent Models

Here's how to create feeds from different Eloquent models:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Controller;
use Illuminate\Http\Response;
use Rumenx\Feed\FeedFactory;
use App\Models\Post;
use App\Models\News;
use App\Models\Product;

class FeedController extends Controller
{
    public function posts(): Response
    {
        return $this->createFeed(
            Post::published()->latest()->limit(20)->get(),
            'Blog Posts',
            'Latest blog posts',
            'posts.show'
        );
    }

    public function news(): Response 
    {
        return $this->createFeed(
            News::published()->latest()->limit(20)->get(),
            'News Updates', 
            'Latest news and updates',
            'news.show'
        );
    }

    public function products(): Response
    {
        return $this->createFeed(
            Product::available()->latest()->limit(50)->get(),
            'New Products',
            'Latest products in our store',
            'products.show'
        );
    }

    private function createFeed($items, string $title, string $description, string $routeName): Response
    {
        $feed = FeedFactory::create();
        $feed->setTitle(config('app.name') . ' - ' . $title);
        $feed->setDescription($description);
        $feed->setLink(url('/'));
        $feed->setLanguage(app()->getLocale());

        foreach ($items as $item) {
            $feed->addItem([
                'title' => $item->title,
                'author' => $item->author->name ?? 'Unknown',
                'link' => route($routeName, $item->slug),
                'pubdate' => $item->created_at,
                'description' => $item->excerpt ?? $item->description ?? str_limit($item->content, 300),
                'category' => $item->categories?->pluck('name')->toArray() ?? []
            ]);
        }

        return response($feed->render('rss'), 200, [
            'Content-Type' => 'application/rss+xml; charset=utf-8'
        ]);
    }
}

Feed Discovery Links

Add feed discovery links to your layout:

In your Blade layout (resources/views/layouts/app.blade.php):

<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>{{ config('app.name', 'Laravel') }}</title>
    
    <!-- Feed Discovery -->
    <link rel="alternate" type="application/rss+xml" title="{{ config('app.name') }} - RSS Feed" href="{{ route('feed.rss') }}">
    <link rel="alternate" type="application/atom+xml" title="{{ config('app.name') }} - Atom Feed" href="{{ route('feed.atom') }}">
</head>
<body>
    @yield('content')
</body>
</html>

Or generate links dynamically:

// In a controller or view
use Rumenx\Feed\Feed;

// Generate feed discovery links
echo Feed::link(route('feed.rss'), 'rss', config('app.name') . ' RSS Feed');
echo Feed::link(route('feed.atom'), 'atom', config('app.name') . ' Atom Feed');

Advanced Laravel Example with Caching

Laravel example with Redis caching and error handling:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Controller;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Rumenx\Feed\FeedFactory;
use App\Models\Post;

class FeedController extends Controller
{
    public function posts(): Response
    {
        try {
            // Cache feed for 1 hour
            $feedXml = Cache::remember('blog-feed-rss', 3600, function () {
                return $this->generatePostsFeed();
            });

            return response($feedXml, 200, [
                'Content-Type' => 'application/rss+xml; charset=utf-8',
                'Cache-Control' => 'public, max-age=3600'
            ]);
        } catch (\Exception $e) {
            Log::error('Feed generation failed: ' . $e->getMessage());
            
            return response('Feed temporarily unavailable', 500);
        }
    }

    private function generatePostsFeed(): string
    {
        $feed = FeedFactory::create();
        $feed->setTitle(config('app.name') . ' - Blog Posts');
        $feed->setDescription('Latest posts from our blog');
        $feed->setLink(url('/'));
        $feed->setLanguage(config('app.locale', 'en'));

        $posts = Post::with(['author', 'categories'])
            ->published()
            ->orderBy('created_at', 'desc')
            ->limit(20)
            ->get();

        foreach ($posts as $post) {
            $feed->addItem([
                'title' => $post->title,
                'author' => $post->author->name,
                'link' => route('posts.show', $post->slug),
                'pubdate' => $post->created_at->toISOString(),
                'description' => $post->excerpt,
                'category' => $post->categories->pluck('name')->toArray(),
                'guid' => route('posts.show', $post->slug)
            ]);
        }

        return $feed->render('rss');
    }
}

Multiple Feeds with Different Categories

Create specialized feeds for different content types:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Controller;
use Illuminate\Http\Response;
use Rumenx\Feed\FeedFactory;
use App\Models\Post;

class FeedController extends Controller
{
    public function technology(): Response
    {
        return $this->categoryFeed('technology', 'Technology Posts');
    }

    public function tutorials(): Response
    {
        return $this->categoryFeed('tutorials', 'Tutorial Posts');
    }

    public function news(): Response
    {
        return $this->categoryFeed('news', 'News Updates');
    }

    private function categoryFeed(string $categorySlug, string $title): Response
    {
        $feed = FeedFactory::create();
        $feed->setTitle(config('app.name') . ' - ' . $title);
        $feed->setDescription($title . ' from ' . config('app.name'));
        $feed->setLink(url('/category/' . $categorySlug));

        $posts = Post::whereHas('categories', function ($query) use ($categorySlug) {
                $query->where('slug', $categorySlug);
            })
            ->published()
            ->latest()
            ->limit(20)
            ->get();

        foreach ($posts as $post) {
            $feed->addItem([
                'title' => $post->title,
                'author' => $post->author->name,
                'link' => route('posts.show', $post->slug),
                'pubdate' => $post->created_at,
                'description' => $post->excerpt,
                'category' => [$title]
            ]);
        }

        return response($feed->render('rss'), 200, [
            'Content-Type' => 'application/rss+xml; charset=utf-8'
        ]);
    }
}

Podcast Feed Example

Create a podcast feed with media enclosures:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Controller;
use Illuminate\Http\Response;
use Rumenx\Feed\FeedFactory;
use App\Models\Episode;

class PodcastController extends Controller
{
    public function feed(): Response
    {
        $feed = FeedFactory::create();
        $feed->setTitle('My Podcast');
        $feed->setDescription('Weekly episodes about technology and programming');
        $feed->setLink(route('podcast.index'));
        $feed->setLanguage('en');

        $episodes = Episode::published()
            ->orderBy('published_at', 'desc')
            ->limit(50)
            ->get();

        foreach ($episodes as $episode) {
            $feed->addItem([
                'title' => $episode->title,
                'author' => 'Podcast Host',
                'link' => route('podcast.episode', $episode->slug),
                'pubdate' => $episode->published_at,
                'description' => $episode->description,
                'enclosure' => [
                    'url' => $episode->audio_url,
                    'type' => 'audio/mpeg',
                    'length' => $episode->file_size
                ]
            ]);
        }

        return response($feed->render('rss'), 200, [
            'Content-Type' => 'application/rss+xml; charset=utf-8'
        ]);
    }
}

Artisan Command for Feed Generation

Create an Artisan command to generate static feed files:

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Rumenx\Feed\FeedFactory;
use App\Models\Post;

class GenerateFeeds extends Command
{
    protected $signature = 'feeds:generate';
    protected $description = 'Generate static RSS and Atom feed files';

    public function handle(): int
    {
        $this->info('Generating RSS feeds...');

        // Generate RSS feed
        $rssFeed = $this->createFeed('rss');
        Storage::disk('public')->put('feed.rss', $rssFeed);
        
        // Generate Atom feed
        $atomFeed = $this->createFeed('atom');
        Storage::disk('public')->put('feed.atom', $atomFeed);

        $this->info('Feeds generated successfully!');
        return 0;
    }

    private function createFeed(string $format): string
    {
        $feed = FeedFactory::create();
        $feed->setTitle(config('app.name'));
        $feed->setDescription('Latest posts from ' . config('app.name'));
        $feed->setLink(url('/'));

        $posts = Post::published()->latest()->limit(20)->get();

        foreach ($posts as $post) {
            $feed->addItem([
                'title' => $post->title,
                'author' => $post->author->name,
                'link' => route('posts.show', $post->slug),
                'pubdate' => $post->created_at,
                'description' => $post->excerpt
            ]);
        }

        return $feed->render($format);
    }
}

Testing Feed Controllers

Example PHPUnit test for your feed controller:

<?php

namespace Tests\Feature;

use Tests\TestCase;
use App\Models\Post;
use App\Models\User;

class FeedControllerTest extends TestCase
{
    public function test_rss_feed_returns_valid_xml(): void
    {
        // Create test data
        $author = User::factory()->create();
        $posts = Post::factory()->count(5)->create([
            'author_id' => $author->id,
            'published' => true
        ]);

        // Request feed
        $response = $this->get('/feed');

        $response->assertStatus(200);
        $response->assertHeader('content-type', 'application/rss+xml; charset=utf-8');
        
        // Verify XML structure
        $xml = simplexml_load_string($response->getContent());
        $this->assertNotFalse($xml);
        $this->assertEquals('rss', $xml->getName());
        $this->assertCount(5, $xml->channel->item);
    }
}

Next Steps

Clone this wiki locally