Installation:
composer require watson/sitemap
Publish the config file (optional):
php artisan vendor:publish --provider="Watson\Sitemap\SitemapServiceProvider"
First Use Case:
Generate a basic sitemap for a Post model:
use Watson\Sitemap\SitemapGenerator;
use Watson\Sitemap\Items\Url;
$sitemap = SitemapGenerator::create()
->get('posts')
->setCallback(function () {
return Post::all()->map(function ($post) {
return Url::create('/posts/' . $post->slug)
->setLastModificationDate($post->updated_at)
->setChangeFrequency(Url::CHANGE_FREQUENCY_DAILY)
->setPriority(0.8);
});
});
return $sitemap->render();
Where to Look First:
config/sitemap.php for configuration options.app/Http/Controllers/SitemapController.php (if using the default route).Dynamic Sitemap Generation:
Use setCallback() to dynamically fetch URLs from your database or API:
$sitemap->setCallback(function () {
return Cache::remember('sitemap_posts', now()->addHours(6), function () {
return Post::published()->get()->map(fn ($post) => Url::create(...));
});
});
Sitemap Indexes: Split large sitemaps into indexes (Google recommends <50,000 URLs per sitemap):
$index = SitemapGenerator::create('index')
->setCallback(function () {
return [
Url::create('/sitemap-posts-1.xml'),
Url::create('/sitemap-posts-2.xml'),
];
});
Route-Based Sitemaps: Generate sitemaps from Laravel routes:
$sitemap->get('routes')->setCallback(function () {
return collect(Route::getRoutes()->getRoutes())->map(fn ($route) => Url::create($route->uri));
});
Integration with Laravel Routes:
Add a route in routes/web.php:
Route::get('/sitemap.xml', [SitemapController::class, 'index']);
Conditional URL Generation: Use closures to conditionally include URLs:
$sitemap->setCallback(function () {
return Post::all()->filter(fn ($post) => $post->isPublic)->map(...);
});
Priority & Frequency: Set dynamic priorities/frequencies based on business logic:
$priority = $post->isFeatured ? 1.0 : 0.5;
Url::create(...)->setPriority($priority);
Caching: Cache generated sitemaps to reduce server load:
$sitemap->setCallback(function () {
return Cache::remember('sitemap_cache_key', now()->addHours(1), function () {
return Post::all()->map(...);
});
});
Custom XML Attributes:
Extend Url to add custom attributes:
class CustomUrl extends Url {
public function setVideo($videoUrl) {
$this->addAttribute('video', $videoUrl);
return $this;
}
}
Memory Limits: Generating large sitemaps (>10,000 URLs) may hit PHP memory limits. Use chunking or caching:
$sitemap->setCallback(function () {
return Post::chunk(1000, function ($posts) {
return $posts->map(...);
});
});
LastModificationDate:
Ensure dates are in DateTime format or ISO-8601 strings. Avoid timestamps:
// Bad: $post->updated_at (Carbon instance)
// Good:
Url::create(...)->setLastModificationDate($post->updated_at->toAtomString());
Duplicate URLs:
Use setChangeFrequency(Url::CHANGE_FREQUENCY_YEARLY) for static pages to avoid penalties.
Route Caching:
If using get('routes'), clear route cache (php artisan route:clear) after changes.
Index Depth: Google recommends keeping sitemap indexes shallow (no nested indexes).
Validate XML: Use an online validator (e.g., XML Validation) to check for malformed tags. Common issues:
lastmod or priority attributes.http://example.com instead of /).Log Generation:
Enable logging in config/sitemap.php:
'log' => true,
Check storage/logs/laravel.log for errors.
Default Options:
Override defaults in config/sitemap.php:
'default_priority' => 0.5,
'default_change_frequency' => \Watson\Sitemap\Items\Url::CHANGE_FREQUENCY_WEEKLY,
Custom Storage: Save sitemaps to disk or S3:
$sitemap->store('sitemap.xml', 'public');
// Or with S3:
$sitemap->store('sitemap.xml', 's3', ['disk' => 's3']);
Locale Support: Generate multilingual sitemaps by prefixing URLs:
Url::create('/en/posts/' . $post->slug)->setLanguage('en');
Custom Items:
Extend the Url class for non-standard sitemap types (e.g., ImageUrl):
class ImageUrl extends \Watson\Sitemap\Items\Url {
public function setImageLoc($loc) {
$this->addAttribute('image:loc', $loc);
return $this;
}
}
Event Listeners: Trigger sitemap regeneration after model updates:
Post::observe(PostObserver::class);
class PostObserver {
public function saved(Post $post) {
event(new PostUpdated($post));
}
}
// In an event listener:
event(PostUpdated::class, function () {
Cache::forget('sitemap_cache_key');
});
Testing: Mock the sitemap generator in tests:
$this->partialMock(SitemapGenerator::class, ['render'])
->shouldReceive('render')
->once()
->andReturn('<urlset>...</urlset>');
How can I help you explore Laravel packages today?