Installation:
composer require anh/feed-builder:~1.0
Add to composer.json under require if not using global install.
First Use Case: Generate a basic RSS/Atom feed from an array:
use Anh\FeedBuilder\FeedBuilder;
$feed = (new FeedBuilder())
->setType('atom') // or 'rss'
->fromArray([
'title' => 'My Blog',
'link' => ['href' => url('/feed')],
'entry-1' => [
'title' => 'First Post',
'link' => url('/post/1'),
],
]);
echo $feed; // Outputs XML
Key Files:
vendor/anh/feed-builder/src/ for core logic.FeedBuilder.php for API reference.Data-Driven Feed Generation:
Use fromArray() for structured data (e.g., Eloquent collections):
$posts = Post::all()->map(fn($p) => [
'title' => $p->title,
'link' => url("/posts/{$p->id}"),
'id' => $p->slug,
'content' => $p->body,
]);
$feed->fromArray([
'title' => 'Blog Feed',
...$posts->pluck('title', 'link', 'id', 'content')->toArray(),
]);
Dynamic Entry Handling: Loop through models/collections to build entries:
foreach ($posts as $index => $post) {
$feed->addEntry([
'title' => $post->title,
'link' => url("/posts/{$post->id}"),
'id' => "tag:{$post->id},{$post->created_at->timestamp}",
]);
}
Validation Integration: Validate before output:
if (!$feed->validate()) {
Log::error('Feed validation failed', ['errors' => $feed->getErrors()]);
abort(500, 'Invalid feed generated');
}
Service Provider Binding:
Bind FeedBuilder in AppServiceProvider:
$this->app->bind(FeedBuilder::class, function ($app) {
return new FeedBuilder(config('feed.type', 'atom'));
});
Route Integration:
Route::get('/feed', function () {
$feed = app(FeedBuilder::class)
->setType(config('feed.type'))
->fromArray($this->getFeedData());
return response($feed, 200, ['Content-Type' => 'application/xml']);
});
Configuration:
Use config/feed.php to centralize settings:
return [
'type' => env('FEED_TYPE', 'atom'),
'default_title' => 'My Site Feed',
'default_link' => '/',
];
Validation Incompleteness:
try-catch for validate():
try {
$feed->validate();
} catch (\Exception $e) {
// Fallback to external validation
}
Date Formatting:
'tomorrow') may fail validation. Use ISO 8601:
'updated' => now()->toAtomString(), // Atom
'pubDate' => now()->toRfc822String(), // RSS
Entry Naming:
item keys (e.g., item-1, item2), while Atom uses entry-*. Mixing formats breaks feeds.addEntry() for clarity.HTML Content:
<content> to specify type (e.g., html or xhtml). Omitting it defaults to text:
'content' => [
'type' => 'html',
'<div>Safe HTML</div>',
],
Inspect Raw XML:
$xml = $feed->getXml();
file_put_contents(storage_path('logs/feed.xml'), $xml);
Error Handling:
$feed->getErrors() for validation failures.Log::debug('Feed input', ['data' => $feed->getData()]);
Custom Validators:
Extend Anh\FeedBuilder\Validator\ValidatorInterface for domain-specific rules.
Template Overrides:
Override XML templates in vendor/anh/feed-builder/src/Resources/views/ by publishing assets:
php artisan vendor:publish --tag=feed-builder-views
Caching:
Cache generated feeds (e.g., with Cache::remember):
return Cache::remember('feed_xml', now()->addHours(1), function () {
return app(FeedBuilder::class)->fromArray($data);
});
Use addEntry() for Dynamic Entries:
Avoid array key collisions with programmatic entries:
$feed->addEntry(['title' => 'Dynamic Post', 'link' => url('/dynamic')]);
Leverage Laravel Collections: Transform collections directly:
$feed->fromArray($posts->map->toArray());
Test with Real Data: Validate against FeedValidator.org for edge cases.
How can I help you explore Laravel packages today?