Search by

markocupic / rss-feed-generator-bundle

markocupic

Simple RSS feed generator. Generate RSS feeds inside a symfony controller.

Package info

github.com/markocupic/rss-feed-generator-bundle

Type:symfony-bundle

pkg:composer/markocupic/rss-feed-generator-bundle

Statistics

Installs: 10 104

Dependents: 2

Suggesters: 0

Stars: 2

Open Issues: 0

1.2.0 2026-10-10 11:53 UTC

README

RSS Feed Generator Bundle

Use this bundle to generate rss feeds inside your Symfony application.

❤ Many thanks to @eko (Vincent Composieux) for giving me the inspiration to program this bundle. https://github.com/eko/FeedBundle.

Requirements

  • PHP ^8.3
  • Symfony ^6.4 || ^7.0 || ^8.0

Installation

composer require markocupic/rss-feed-generator-bundle

Option A: Add this to your config/bundles.php.

<?php

return [
    // ...
    Markocupic\RssFeedGeneratorBundle\MarkocupicRssFeedGeneratorBundle::class => ['all' => true],
];

Option B: In a Contao ❤ environment register the rss feed generator bundle in the Contao Manager Plugin class of your bundle.

<?php

declare(strict_types=1);

namespace Contao\CoreBundle\ContaoManager;
use Contao\CoreBundle\ContaoCoreBundle;
use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
use Contao\ManagerPlugin\Config\ConfigPluginInterface;
use Contao\ManagerPlugin\Routing\RoutingPluginInterface;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\Config\Loader\LoaderResolverInterface;
use Symfony\Component\HttpKernel\KernelInterface;
use Markocupic\RssFeedGeneratorBundle\MarkocupicRssFeedGeneratorBundle;
use Acme\MyBundle\AcmeMyBundleBundle;

class Plugin implements BundlePluginInterface, RoutingPluginInterface, ConfigPluginInterface
{
    /**
     * {@inheritdoc}
     */
    public function getBundles(ParserInterface $parser): array
    {
        return [
            // Register RSS feed generator bundle
            BundleConfig::create(MarkocupicRssFeedGeneratorBundle::class),
            // register other bundles
            BundleConfig::create(AcmeMyBundle::class)
                ->setLoadAfter([MarkocupicRssFeedGeneratorBundle::class, ContaoCoreBundle::class]),
        ];
    }

Use dependency injection (autowiring) to require the feed factory in your controller.

Create the feed

// Use the feed factory to generate the feed object
$rss = $this->feedFactory->createFeed(\Markocupic\RssFeedGeneratorBundle\Feed\Feed::ENCODING_UTF8);

 // Add one or more attributes to the root element
$rss->setRootAttributes([
    'xmlns:tourdb' => 'https://acme.com/schema/tourdbrss/1.0',
    'xmlns:atom'=>'http://www.w3.org/2005/Atom',
]);

// Add one or more attributes to the channel element
$rss->setChannelAttributes([
    'foo' => 'bar',
]);

Add feed Channel elements

Use the Item class inside the feed factory method FeedFactory::addChannelField().

The Item::__constructor($elementName, $strValue, $arrOptions, $arrAttributes) takes four arguments:

  1. (string) element name
  2. (string) content
  3. optional: (array) options (at the moment cdata, and filters)
  4. optional: (array) with attributes
$rss->addChannelField(
    new Item('title', 'Demo feed')
);

$rss->addChannelField(
    new Item('link', 'https://foobar.ch')
);

Make cdata elements and insert attributes:

// Add CDATA element and an attribute
$rss->addChannelField(
    new Item('description', 'Check our news feed and have fun!', ['cdata' => true], ['attrName' => 'Here comes the attribute value'])
);

Filter or replace content

// filter or replace values (regular expressions)
$arrFilter = ['/Ferrari/' => 'Italia', '/car/' => 'country'];
$rss->addChannelField(
    new Item('description', 'Ferrari is my favourite car!', ['filter' => $arrFilter])
);
// Will result in:
// <description>Italia is my favourite country!</description>

Add channel items

Use FeedFactory::addChannelItemField(), ItemGroup() and Item() to generate channel items.

The ItemGroup::__constructor($elementName, $arrItemObjects, $arrAttributes) takes three arguments:

  1. (string) element name
  2. (array) with Item objects
  3. optional: (array) with attributes
// Retrieve data from database and add items
foreach ($this->getEvents() as $arrEvent) {
    // Use a new instance of ItemGroup to add a collection of items all of the same level.
    $rss->addChannelItemField(
        new ItemGroup('item', [
            new Item('title', $arrEvent['title']),
            new Item('link', $arrEvent['link']),
            new Item('description', $arrEvent['description'], ['cdata' => true]),
            new Item('pubDate', date('r', (int) $arrEvent['tstamp'])),
            new Item('author', $arrEvent['author']),
            new Item('guid', $arrEvent['uuid']),
            new Item('tourdb:startdate', date('Y-m-d', (int) $arrEvent['startDate'])),
            new Item('tourdb:enddate', date('Y-m-d', (int) $arrEvent['endDate'])),
        ])
    );
}

Nested items

// Append nested items with ItemGroup.
$rss->addChannelItemField(
    new ItemGroup('item', [
        new Item('title', 'Title'),
        new Item('link', 'https://foo.bar'),
        new ItemGroup('nestedItems', [
            new Item('subitem', 'Some content'),
            new Item('subitem', 'Some content'),
        ], ['foo'=> 'bar']),
    ])
);

Result:

<item>
    <title>Title</title>
    <link>https://foo.bar</link>
    <nestedItem foo="bar">
        <subitem>Some content</subitem>
        <subitem>Some content</subitem>
    </nestedItem>
</item>

Render and send content to the browser.

return $rss->render();

Render and save content to the filesystem.

return $rss->render('public/share/myfeed.xml');

Generate RSS 2.0 feed inside a controller in a Symfony bundle

<?php

declare(strict_types=1);

namespace Acme\DemoBundle\Controller\Feed;

use Doctrine\DBAL\Connection;
use Markocupic\RssFeedGeneratorBundle\Feed\Feed;
use Markocupic\RssFeedGeneratorBundle\Feed\FeedFactory;
use Markocupic\RssFeedGeneratorBundle\Item\Item;
use Markocupic\RssFeedGeneratorBundle\Item\ItemGroup;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

#[Route('/_rssfeed', name: 'rss_feed')]
class FeedController
{
    public function __construct(
        private readonly FeedFactory $feedFactory,
        private readonly Connection $connection,
        #[Autowire('%kernel.project_dir%')]
        private readonly string $projectDir,
    ) {
    }

    public function __invoke(): Response
    {
        $rss = $this->feedFactory->createFeed(Feed::ENCODING_UTF8);

        $rss->addChannelField(new Item('title', 'Acme news'));
        $rss->addChannelField(new Item('description', 'Enjoy our news.'));
        $rss->addChannelField(new Item('link', 'https://acme.com'));
        $rss->addChannelField(new Item('language', 'de'));
        $rss->addChannelField(new Item('pubDate', date('r', time() - 3600)));

        // Retrieve data from the database and add some channel items
        $events = $this->connection->fetchAllAssociative('SELECT * FROM tl_calendar_events WHERE published = 1 ORDER BY startDate DESC');

        foreach ($events as $arrEvent) {
            $rss->addChannelItemField(
                new ItemGroup('item', [
                    new Item('title', $arrEvent['title']),
                    new Item('description', (string) $arrEvent['teaser'], ['cdata' => true]),
                    new Item('pubDate', date('r', (int) $arrEvent['tstamp'])),
                ])
            );
        }

        return $rss->render($this->projectDir.'/public/share/rss.xml');
    }
}

Filter & search and replace strings

The extension will filter by default some characters. Linebreaks will be replaced with a whitespace, etc. Please have a look at the Plugin Configuration.

Overriding these defaults is pretty easy and can be done in config/config.yaml. Please use regular expressions for the search patterns.

# config/config.yaml
markocupic_rss_feed_generator:
  filter:
    '/</': '&lt;'
    '/[\n\r]+/': ' '
    '/&#40;/': '('
    '/&#41;/': ')'
    '/\[-\]/': ''
    '/\&shy;/': ''
    '/\[nbsp\]/': ' '
    '/&nbsp;/': ' '
    '/&/': '&amp;'