Skip to content

Multiple Feeds

Rumen Damyanov edited this page Jul 29, 2025 · 2 revisions

Multiple Feeds Example

This guide shows how to create and manage multiple feeds for different content types, categories, or audiences.

Why Multiple Feeds?

Multiple feeds are useful when you have:

  • Different content types (blog posts, news, tutorials, products)
  • Multiple categories or topics
  • Different audiences (general public, technical users, etc.)
  • Various content formats (articles, podcasts, videos)

Basic Multiple Feeds Setup

Here's how to create different feeds for different content types:

<?php
require 'vendor/autoload.php';

use Rumenx\Feed\FeedFactory;

// Get the requested feed type
$feedType = $_GET['type'] ?? 'blog';

switch ($feedType) {
    case 'news':
        generateNewsFeed();
        break;
    case 'tutorials':
        generateTutorialsFeed();
        break;
    case 'products':
        generateProductsFeed();
        break;
    case 'blog':
    default:
        generateBlogFeed();
        break;
}

function generateBlogFeed(): void
{
    $feed = FeedFactory::create();
    $feed->setTitle('My Website - Blog Posts');
    $feed->setDescription('Latest blog posts and articles');
    $feed->setLink('https://example.com/blog');
    $feed->setLanguage('en');

    // Sample blog posts data
    $posts = [
        [
            'title' => 'Getting Started with PHP',
            'author' => 'John Doe',
            'link' => 'https://example.com/blog/getting-started-php',
            'pubdate' => '2025-01-15T10:00:00Z',
            'description' => 'A beginner-friendly guide to PHP programming.'
        ],
        [
            'title' => 'Advanced Laravel Techniques',
            'author' => 'Jane Smith',
            'link' => 'https://example.com/blog/advanced-laravel',
            'pubdate' => '2025-01-14T14:30:00Z',
            'description' => 'Explore advanced patterns and techniques in Laravel.'
        ]
    ];

    foreach ($posts as $post) {
        $feed->addItem($post);
    }

    outputFeed($feed);
}

function generateNewsFeed(): void
{
    $feed = FeedFactory::create();
    $feed->setTitle('My Website - News Updates');
    $feed->setDescription('Latest company and industry news');
    $feed->setLink('https://example.com/news');
    $feed->setLanguage('en');

    // Sample news data
    $news = [
        [
            'title' => 'Company Launches New Product',
            'author' => 'Press Team',
            'link' => 'https://example.com/news/new-product-launch',
            'pubdate' => '2025-01-16T09:00:00Z',
            'description' => 'We are excited to announce our latest product innovation.'
        ],
        [
            'title' => 'Industry Conference Recap',
            'author' => 'Event Team',
            'link' => 'https://example.com/news/conference-recap',
            'pubdate' => '2025-01-15T16:00:00Z',
            'description' => 'Key insights from this years biggest tech conference.'
        ]
    ];

    foreach ($news as $item) {
        $feed->addItem($item);
    }

    outputFeed($feed);
}

function generateTutorialsFeed(): void
{
    $feed = FeedFactory::create();
    $feed->setTitle('My Website - Tutorials');
    $feed->setDescription('Step-by-step programming tutorials');
    $feed->setLink('https://example.com/tutorials');
    $feed->setLanguage('en');

    // Sample tutorial data
    $tutorials = [
        [
            'title' => 'Building a REST API with PHP',
            'author' => 'Tech Team',
            'link' => 'https://example.com/tutorials/rest-api-php',
            'pubdate' => '2025-01-13T12:00:00Z',
            'description' => 'Learn how to build a robust REST API using modern PHP.',
            'category' => ['PHP', 'API', 'Backend']
        ],
        [
            'title' => 'Database Design Best Practices',
            'author' => 'Database Expert',
            'link' => 'https://example.com/tutorials/database-design',
            'pubdate' => '2025-01-12T10:00:00Z',
            'description' => 'Essential principles for designing efficient databases.',
            'category' => ['Database', 'MySQL', 'Design']
        ]
    ];

    foreach ($tutorials as $tutorial) {
        $feed->addItem($tutorial);
    }

    outputFeed($feed);
}

function generateProductsFeed(): void
{
    $feed = FeedFactory::create();
    $feed->setTitle('My Store - New Products');
    $feed->setDescription('Latest products in our store');
    $feed->setLink('https://example.com/products');
    $feed->setLanguage('en');

    // Sample product data
    $products = [
        [
            'title' => 'Premium PHP Course Bundle',
            'author' => 'Store Admin',
            'link' => 'https://example.com/products/php-course-bundle',
            'pubdate' => '2025-01-16T08:00:00Z',
            'description' => 'Complete PHP learning package with 20+ hours of content.',
            'category' => ['Courses', 'PHP', 'Programming']
        ],
        [
            'title' => 'Development Tools License',
            'author' => 'Store Admin',
            'link' => 'https://example.com/products/dev-tools-license',
            'pubdate' => '2025-01-15T12:00:00Z',
            'description' => 'Professional development tools for modern web developers.',
            'category' => ['Tools', 'Software', 'Development']
        ]
    ];

    foreach ($products as $product) {
        $feed->addItem($product);
    }

    outputFeed($feed);
}

function outputFeed(object $feed): void
{
    header('Content-Type: application/rss+xml; charset=utf-8');
    echo $feed->render('rss');
}

URL Structure for Multiple Feeds

Organize your feeds with clean URLs:

https://example.com/feed              # Main blog feed
https://example.com/feed/blog         # Blog posts
https://example.com/feed/news         # News updates  
https://example.com/feed/tutorials    # Tutorials
https://example.com/feed/products     # Products
https://example.com/feed/category/php # PHP category

Database-Driven Multiple Feeds

More advanced example with database integration:

<?php
require 'vendor/autoload.php';

use Rumenx\Feed\FeedFactory;
use PDO;

class MultiFeedGenerator
{
    private PDO $pdo;
    private array $baseConfig;

    public function __construct(PDO $pdo)
    {
        $this->pdo = $pdo;
        $this->baseConfig = [
            'base_url' => 'https://example.com',
            'language' => 'en'
        ];
    }

    public function generateFeed(string $type, ?string $category = null): string
    {
        switch ($type) {
            case 'posts':
                return $this->generatePostsFeed($category);
            case 'news':
                return $this->generateNewsFeed();
            case 'tutorials':
                return $this->generateTutorialsFeed($category);
            case 'products':
                return $this->generateProductsFeed($category);
            default:
                throw new InvalidArgumentException("Unknown feed type: {$type}");
        }
    }

    private function generatePostsFeed(?string $category = null): string
    {
        $feed = FeedFactory::create();
        $feed->setTitle('My Blog' . ($category ? " - {$category}" : ''));
        $feed->setDescription('Latest blog posts' . ($category ? " in {$category}" : ''));
        $feed->setLink($this->baseConfig['base_url'] . '/blog');
        $feed->setLanguage($this->baseConfig['language']);

        $posts = $this->fetchPosts($category);

        foreach ($posts as $post) {
            $feed->addItem([
                'title' => $post['title'],
                'author' => $post['author_name'],
                'link' => $this->baseConfig['base_url'] . '/blog/' . $post['slug'],
                'pubdate' => $post['created_at'],
                'description' => $post['excerpt'],
                'category' => explode(',', $post['categories'] ?? '')
            ]);
        }

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

    private function generateNewsFeed(): string
    {
        $feed = FeedFactory::create();
        $feed->setTitle('My Website - News');
        $feed->setDescription('Latest news and announcements');
        $feed->setLink($this->baseConfig['base_url'] . '/news');
        $feed->setLanguage($this->baseConfig['language']);

        $news = $this->fetchNews();

        foreach ($news as $item) {
            $feed->addItem([
                'title' => $item['title'],
                'author' => $item['author_name'],
                'link' => $this->baseConfig['base_url'] . '/news/' . $item['slug'],
                'pubdate' => $item['published_at'],
                'description' => $item['summary']
            ]);
        }

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

    private function generateTutorialsFeed(?string $category = null): string
    {
        $feed = FeedFactory::create();
        $feed->setTitle('My Website - Tutorials' . ($category ? " - {$category}" : ''));
        $feed->setDescription('Programming tutorials and guides');
        $feed->setLink($this->baseConfig['base_url'] . '/tutorials');
        $feed->setLanguage($this->baseConfig['language']);

        $tutorials = $this->fetchTutorials($category);

        foreach ($tutorials as $tutorial) {
            $feed->addItem([
                'title' => $tutorial['title'],
                'author' => $tutorial['author_name'],
                'link' => $this->baseConfig['base_url'] . '/tutorials/' . $tutorial['slug'],
                'pubdate' => $tutorial['created_at'],
                'description' => $tutorial['description'],
                'category' => explode(',', $tutorial['tags'] ?? '')
            ]);
        }

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

    private function generateProductsFeed(?string $category = null): string
    {
        $feed = FeedFactory::create();
        $feed->setTitle('My Store - Products' . ($category ? " - {$category}" : ''));
        $feed->setDescription('Latest products and updates');
        $feed->setLink($this->baseConfig['base_url'] . '/products');
        $feed->setLanguage($this->baseConfig['language']);

        $products = $this->fetchProducts($category);

        foreach ($products as $product) {
            $feed->addItem([
                'title' => $product['name'],
                'author' => 'Store Team',
                'link' => $this->baseConfig['base_url'] . '/products/' . $product['slug'],
                'pubdate' => $product['created_at'],
                'description' => $product['description'],
                'category' => [$product['category_name']]
            ]);
        }

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

    private function fetchPosts(?string $category = null): array
    {
        $sql = "
            SELECT p.title, p.slug, p.excerpt, p.created_at, u.name as author_name,
                   GROUP_CONCAT(c.name) as categories
            FROM posts p
            JOIN users u ON p.author_id = u.id
            LEFT JOIN post_categories pc ON p.id = pc.post_id
            LEFT JOIN categories c ON pc.category_id = c.id
            WHERE p.published = 1
        ";

        $params = [];
        if ($category) {
            $sql .= " AND c.slug = ?";
            $params[] = $category;
        }

        $sql .= " GROUP BY p.id ORDER BY p.created_at DESC LIMIT 20";

        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    private function fetchNews(): array
    {
        $stmt = $this->pdo->prepare("
            SELECT n.title, n.slug, n.summary, n.published_at, u.name as author_name
            FROM news n
            JOIN users u ON n.author_id = u.id
            WHERE n.published = 1
            ORDER BY n.published_at DESC
            LIMIT 15
        ");
        $stmt->execute();
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    private function fetchTutorials(?string $category = null): array
    {
        $sql = "
            SELECT t.title, t.slug, t.description, t.created_at, u.name as author_name,
                   GROUP_CONCAT(tag.name) as tags
            FROM tutorials t
            JOIN users u ON t.author_id = u.id
            LEFT JOIN tutorial_tags tt ON t.id = tt.tutorial_id
            LEFT JOIN tags tag ON tt.tag_id = tag.id
            WHERE t.published = 1
        ";

        $params = [];
        if ($category) {
            $sql .= " AND tag.slug = ?";
            $params[] = $category;
        }

        $sql .= " GROUP BY t.id ORDER BY t.created_at DESC LIMIT 10";

        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }

    private function fetchProducts(?string $category = null): array
    {
        $sql = "
            SELECT p.name, p.slug, p.description, p.created_at, c.name as category_name
            FROM products p
            JOIN categories c ON p.category_id = c.id
            WHERE p.active = 1
        ";

        $params = [];
        if ($category) {
            $sql .= " AND c.slug = ?";
            $params[] = $category;
        }

        $sql .= " ORDER BY p.created_at DESC LIMIT 20";

        $stmt = $this->pdo->prepare($sql);
        $stmt->execute($params);
        return $stmt->fetchAll(PDO::FETCH_ASSOC);
    }
}

// Usage
$pdo = new PDO('mysql:host=localhost;dbname=website', $username, $password);
$generator = new MultiFeedGenerator($pdo);

$type = $_GET['type'] ?? 'posts';
$category = $_GET['category'] ?? null;

header('Content-Type: application/rss+xml; charset=utf-8');
echo $generator->generateFeed($type, $category);

Feed Discovery for Multiple Feeds

Add discovery links for all your feeds:

<?php
use Rumenx\Feed\Feed;

function generateFeedDiscoveryLinks(): string
{
    $links = [
        Feed::link('https://example.com/feed', 'rss', 'Main Feed'),
        Feed::link('https://example.com/feed/blog', 'rss', 'Blog Posts'),
        Feed::link('https://example.com/feed/news', 'rss', 'News Updates'),
        Feed::link('https://example.com/feed/tutorials', 'rss', 'Tutorials'),
        Feed::link('https://example.com/feed/products', 'atom', 'Products')
    ];

    return implode("\n", $links);
}

// In your HTML <head>
echo generateFeedDiscoveryLinks();

Laravel Multiple Feeds Example

<?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\Tutorial;
use App\Models\Product;

class MultiFeedController extends Controller
{
    public function index(string $type = 'blog', ?string $category = null): Response
    {
        $feedData = $this->getFeedData($type, $category);
        
        $feed = FeedFactory::create();
        $feed->setTitle($feedData['title']);
        $feed->setDescription($feedData['description']);
        $feed->setLink($feedData['link']);
        $feed->setLanguage(app()->getLocale());

        foreach ($feedData['items'] as $item) {
            $feed->addItem($item);
        }

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

    private function getFeedData(string $type, ?string $category = null): array
    {
        switch ($type) {
            case 'news':
                return $this->getNewsData();
                
            case 'tutorials':
                return $this->getTutorialsData($category);
                
            case 'products':
                return $this->getProductsData($category);
                
            case 'blog':
            default:
                return $this->getBlogData($category);
        }
    }

    private function getBlogData(?string $category = null): array
    {
        $query = Post::published()->latest()->limit(20);
        
        if ($category) {
            $query->whereHas('categories', fn($q) => $q->where('slug', $category));
        }
        
        $posts = $query->get();
        
        return [
            'title' => config('app.name') . ' - Blog' . ($category ? " - {$category}" : ''),
            'description' => 'Latest blog posts',
            'link' => route('blog.index'),
            'items' => $posts->map(fn($post) => [
                'title' => $post->title,
                'author' => $post->author->name,
                'link' => route('blog.show', $post->slug),
                'pubdate' => $post->created_at,
                'description' => $post->excerpt,
                'category' => $post->categories->pluck('name')->toArray()
            ])->toArray()
        ];
    }

    private function getNewsData(): array
    {
        $news = News::published()->latest()->limit(15)->get();
        
        return [
            'title' => config('app.name') . ' - News',
            'description' => 'Latest news and updates',
            'link' => route('news.index'),
            'items' => $news->map(fn($item) => [
                'title' => $item->title,
                'author' => $item->author->name,
                'link' => route('news.show', $item->slug),
                'pubdate' => $item->published_at,
                'description' => $item->summary
            ])->toArray()
        ];
    }

    private function getTutorialsData(?string $category = null): array
    {
        $query = Tutorial::published()->latest()->limit(10);
        
        if ($category) {
            $query->whereHas('tags', fn($q) => $q->where('slug', $category));
        }
        
        $tutorials = $query->get();
        
        return [
            'title' => config('app.name') . ' - Tutorials' . ($category ? " - {$category}" : ''),
            'description' => 'Programming tutorials and guides',
            'link' => route('tutorials.index'),
            'items' => $tutorials->map(fn($tutorial) => [
                'title' => $tutorial->title,
                'author' => $tutorial->author->name,
                'link' => route('tutorials.show', $tutorial->slug),
                'pubdate' => $tutorial->created_at,
                'description' => $tutorial->description,
                'category' => $tutorial->tags->pluck('name')->toArray()
            ])->toArray()
        ];
    }

    private function getProductsData(?string $category = null): array
    {
        $query = Product::active()->latest()->limit(20);
        
        if ($category) {
            $query->whereHas('category', fn($q) => $q->where('slug', $category));
        }
        
        $products = $query->get();
        
        return [
            'title' => config('app.name') . ' - Products' . ($category ? " - {$category}" : ''),
            'description' => 'Latest products and updates',
            'link' => route('products.index'),
            'items' => $products->map(fn($product) => [
                'title' => $product->name,
                'author' => 'Store Team',
                'link' => route('products.show', $product->slug),
                'pubdate' => $product->created_at,
                'description' => $product->description,
                'category' => [$product->category->name]
            ])->toArray()
        ];
    }
}

Routes for Multiple Feeds

// routes/web.php
Route::get('/feed/{type?}/{category?}', [MultiFeedController::class, 'index'])
    ->where('type', 'blog|news|tutorials|products')
    ->where('category', '[a-zA-Z0-9-]+')
    ->name('feeds.show');

// Alternative explicit routes
Route::get('/feed', [MultiFeedController::class, 'index'])->name('feeds.main');
Route::get('/feed/blog', [MultiFeedController::class, 'index'])->defaults('type', 'blog');
Route::get('/feed/news', [MultiFeedController::class, 'index'])->defaults('type', 'news');
Route::get('/feed/tutorials', [MultiFeedController::class, 'index'])->defaults('type', 'tutorials');
Route::get('/feed/products', [MultiFeedController::class, 'index'])->defaults('type', 'products');

// Category-specific feeds
Route::get('/feed/blog/{category}', [MultiFeedController::class, 'index'])
    ->defaults('type', 'blog')->name('feeds.blog.category');
Route::get('/feed/tutorials/{category}', [MultiFeedController::class, 'index'])
    ->defaults('type', 'tutorials')->name('feeds.tutorials.category');

Next Steps

Clone this wiki locally