Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Feed Bundle Laravel Package

nekland/feed-bundle

Symfony bundle for generating and serving RSS/Atom syndication feeds. It helps expose blog posts, news, or other dynamic content as feeds with simple integration into Symfony applications and templates.

View on GitHub
Deep Wiki
Context7

Getting Started

1. Install and check constraints first. This bundle targets the Symfony 2.x/3.x era (no Flex recipe, no dependents). Before writing any code:

composer require nekland/feed-bundle

Then open vendor/nekland/feed-bundle/composer.json and confirm the symfony/* and php constraints match your app. If you're on Symfony 4+, expect to either pin an older framework, add a replace/version override, or vendor the bundle into src/ — the bundle's DI extension and service definitions predate autowiring.

2. Register the bundle. Symfony ≤3: add to app/AppKernel.php.

$bundles = [
    // ...
    new Nekland\FeedBundle\NeklandFeedBundle(),
];

Symfony 4+: add to config/bundles.php (only if the constraints allow it).

3. Read the config tree before configuring. The bundle is config-driven, and guessing key names is the fastest way to waste an hour:

bin/console config:dump-reference nekland_feed

That reference is your source of truth for feed names, titles, links, formats, and any item defaults. Mirror it into config/packages/nekland_feed.yaml (or app/config/config.yml):

nekland_feed:
    feeds:
        blog:
            title: "My Blog"
            description: "Latest posts"
            link: "https://example.com/"
            # format / encoding keys: check the dump-reference output

4. Find the services. Service IDs in this bundle use the old underscore style and are not autowirable:

bin/console debug:container | grep -i feed

You're looking for the feed manager (commonly nekland_feed.manager) and any per-feed or factory services. Note the exact IDs; you'll inject them by string.

5. First use case — expose an RSS feed from a controller. Generate the feed from your repository, attach items, return an XML response:

public function feedAction(): Response
{
    $manager = $this->get('nekland_feed.manager'); // verify ID via debug:container
    $feed = $manager->getFeed('blog');              // name from config

    foreach ($this->getDoctrine()->getRepository(Post::class)->findLatest(20) as $post) {
        $item = $feed->createNewItem(); // check method name in vendor source
        $item->setTitle($post->getTitle());
        $item->setLink($this->generateUrl('post_show', ['id' => $post->getId()], true));
        $item->setDescription($post->getExcerpt());
        $item->setDate($post->getPublishedAt());
        $feed->addItem($item);
    }

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

Two things to verify in vendor/nekland/feed-bundle and its nekland/feed dependency before trusting the snippet: the manager's accessor name (getFeed vs createFeed) and the item factory method (createNewItem vs new Item). The underlying feed library's API is small — read it once, then you're done.

Implementation Patterns

Config-per-channel, code-per-content. Use the bundle config for the slow-changing parts of a feed (title, description, canonical link, format) and populate items at request time from your repository. This keeps feed metadata out of controllers and makes adding a second feed (podcast, comments, tag-specific) a config-only change.

Dedicated feed controller, one action per feed. Don't bury feed generation in PostController::show. A FeedController with blogAction(), tagAction($tag) etc. is easier to cache, rate-limit, and test:

#[Route('/feed/{format}', name: 'feed', defaults: ['format' => 'rss'], requirements: ['format' => 'rss|atom'])]

Repository-driven item mapping. Keep a small private method that converts an entity to an item, so both the controller and any console command share it:

private function toItem($feed, Post $post): void
{
    $item = $feed->createNewItem();
    $item->setTitle($post->getTitle());
    $item->setLink($this->generateUrl('post_show', ['id' => $post->getId()], UrlGeneratorInterface::ABSOLUTE_URL));
    $item->setDescription($post->getExcerpt());
    $item->setDate($post->getPublishedAt());
    $feed->addItem($item);
}

Note the explicit ABSOLUTE_URL — relative links break in every reader.

Cache the rendered output, not the object graph. Feed generation is one query plus string building, but readers poll aggressively (some every 5 minutes). Cache the rendered string in Symfony's cache pool keyed by feed name, and invalidate on post publish/update via a Doctrine listener:

$cached = $this->cache->get($key, function (ItemInterface $item) use ($manager) {
    $item->expiresAfter(600);
    return $this->renderFeed($manager);
});
return new Response($cached, 200, ['Content-Type' => 'application/rss+xml; charset=UTF-8']);

Warm the feed on a schedule. Add a console command that calls the same renderFeed() and writes it to a cache/key, wired into cron. This decouples reader traffic from your database and makes the cache stampede problem disappear.

Reuse it in Twig for discovery links. Once the route exists, add the autodiscovery tag to your base layout:

<link rel="alternate" type="application/rss+xml" title="My Blog" href="{{ url('feed', {format: 'rss'}) }}">

Enclosures for media feeds. If the underlying item supports enclosures (podcasts, video), set them from your media entity — this is the one place where "just render the config feed" isn't enough, and it's worth reading the feed library's Item class fully.

Testing pattern. Functional-test the controller, not the library:

$client->request('GET', '/feed/rss');
$this->assertResponseIsSuccessful();
$this->assertStringContainsString('<rss', $client->getResponse()->getContent());
$this->assertStringContainsString($post->getTitle(), $client->getResponse()->getContent());

Gotchas and Tips

It's effectively unmaintained (0 dependents). Treat every version bump as a manual integration test. Keep the bundle behind a thin FeedRenderer interface in your app so replacing it with laminas/laminas-feed or willdurand/feed later is a one-class change, not a refactor.

No autowiring. The services predate type-hinted injection. Inject by ID ($this->get('nekland_feed.manager')) or define an alias in services.yaml pointing at the concrete class. debug:container is non-negotiable here — IDs are guessed wrong constantly.

Config keys are strict and fail at warmup. A typo in nekland_feed.yaml produces a InvalidConfigurationException during cache:clear, not at runtime. That's good — but on a live deploy it means a bad merge blocks the release. Run bin/console lint:yaml config/ in CI.

Date objects, not strings. The feed library expects \DateTime/\DateTimeInterface on items. Passing a formatted string either throws or silently emits a broken <pubDate>, which readers interpret as "now" — causing items to reappear as unread. Always pass the entity's DateTime and let the library format it; set a timezone on your entities.

HTML in descriptions. Most feed libraries escape descriptions by default, so <p> tags render as literal text in readers. Either strip HTML down to plain text (safest, best compatibility) or use the library's CDATA/raw option if it exposes one — check Item's setter signature before assuming.

Relative URLs silently break. Item links and the channel link must be absolute. Use UrlGeneratorInterface::ABSOLUTE_URL explicitly rather than relying on the request context, which is wrong behind proxies unless you've configured trusted_proxies.

Content-Type matters. Return application/rss+xml; charset=UTF-8 (or application/atom+xml for Atom). Serving as text/html makes browsers render the XML as a page; some readers refuse it.

Symfony deprecations will surface as errors. On newer Symfony versions, old service definition syntax and ContainerAware patterns trigger deprecation notices that can fail strict test suites. Run your functional feed test with SYMFONY_DEPRECATIONS_HELPER=disabled initially to distinguish real failures from legacy noise.

Extension points are limited — fork if needed. If you need custom namespaces, iTunes tags, or JSON Feed, the bundle likely won't expose them. The cheapest path is a small local bundle that decorates renderFeed() and post-processes the XML string, rather than patching vendor/.

Debug a broken feed with a real validator. curl -s localhost:8000/feed/rss | xmllint --format - catches malformed XML immediately; then run the output through the W3C Feed Validation Service. Reader bugs are almost always a missing required channel element rather than a logic error in your controller.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
socialdept/atp-orm
cboxdk/laravel-assistant-client
socialdept/atp-resolver
socialdept/tenant-domains
tautvydasr/blade-class-names
socialdept/color-scales
maherelgamil/controller
coleus/widgets
coleus/table
coleus/users
coleus/music
coleus/health
coleus/calendar
coleus/notes
coleus/support
coleus/settings
coleus/apps
seampact/laravel
emmyconceptng/laravel-ng-postcode
jjsquady/round-robin