skuola/sitemap-bundle
Symfony bundle for generating XML sitemaps from your application's routes and content. Configure sitemap sections, URLs, priorities, and change frequencies, then expose a search-engine-ready sitemap.
Read the framework boundary first. This is a Symfony 2 bundle, not a Laravel package. It has no service provider, facade, config publishing, or Blade view. Before anything else, confirm which framework you're actually in:
composer require this into Laravel and call it a day. Use spatie/laravel-sitemap instead.Where to look first, in order:
README.md on github.com/skuola/SitemapBundle — bundle class name, install steps, and the routing import line.composer.json — PHP constraint and Symfony version constraint. Last release was 2016, so expect php: >=5.5 and Symfony 2.8/3.0-era requirements. This is the compatibility gate.DependencyInjection/ and Resources/config/services.* — how the generator/controller is wired, and which service IDs you can override.Controller/ and Resources/views/ — the actual XML-rendering logic and template.Minimal steps (Symfony):
composer require skuola/sitemap-bundle
// app/AppKernel.php
$bundles = [
// ...
new Skuola\SitemapBundle\SkuolaSitemapBundle(), // verify exact namespace in the repo
];
# app/config/routing.yml
sitemap:
resource: "@SkuolaSitemapBundle/Resources/config/routing.xml" # confirm path in repo
First use case: expose /sitemap.xml listing your static pages plus one dynamic entity (e.g. blog posts from a Doctrine repository). Hit the route in a browser, view source, and validate the XML with xmllint --noout sitemap.xml or a sitemap validator. That single loop tells you whether the bundle's URL registration style fits your app.
Determine the URL registration style before writing anything. Symfony 2 sitemap bundles typically take one of two shapes — find out which this one uses by grepping the repo for Provider, Generator, Url, or Annotation:
Url value objects. More code, but the right fit for dynamic content.Static + dynamic hybrid (the common real-world setup):
// Controller or provider service
$urls = [];
// Static pages
foreach (['/', '/about', '/contact'] as $path) {
$urls[] = (new Url())->setLoc($this->generateUrl($path))->setPriority(0.8);
}
// Dynamic: blog posts — select only needed columns to avoid N+1
$posts = $postRepo->createQueryBuilder('p')
->select('p.slug', 'p.updatedAt')
->getQuery()
->getResult();
foreach ($posts as $post) {
$urls[] = (new Url())
->setLoc($this->generateUrl('post_show', ['slug' => $post->getSlug()]))
->setLastmod($post->getUpdatedAt()) // W3C datetime
->setChangefreq('weekly')
->setPriority(0.6);
}
Sitemap index for large sites. Anything over ~50k URLs or 50 MB should be split into sitemap-1.xml, sitemap-2.xml, … and sitemap.xml becomes an index referencing them. Most 2016-era Symfony bundles don't do this for you — hand-roll it or use a paginated provider.
Caching is mandatory, not optional. Sitemap generation is an expensive full-table scan plus route generation. Wrap it:
# services.yml
sitemap.cached_generator:
class: AppBundle\Sitemap\CachedGenerator
decorates: skuola_sitemap.generator # verify the real service id in the repo
arguments: ['@sitemap.cached_generator.inner', '@cache.app']
Invalidate on content publish via a Doctrine postPersist/postUpdate listener or a kernel event — not on every request.
Prefer console-driven static output. For anything with more than a few hundred URLs, add a bin/console app:sitemap:generate command that writes web/sitemap.xml and let nginx/Apache serve it. Skip the controller entirely in that pattern. It removes generation from the request path and makes regeneration debuggable.
Multilingual (hreflang). If you're running a localized site (JMS i18n routing or similar), generate one sitemap per locale plus an index. Most bundles of this vintage have no hreflang support — verify before promising it to a stakeholder.
spatie/laravel-sitemap: actively maintained, Laravel 10/11 compatible, supports sitemap indexes plus image/news sitemaps. A scheduled console command calling ->writeToFile() is ten lines.bin/console debug:container | grep sitemap (or container:debug on 2.x) settles it fast.&) must be escaped or parsers reject the file. Check whether the bundle's Twig template uses {{ url }} (autoescaped) or {{ url|raw }} (not). Fix it yourself if unsure.lastmod format. Must be W3C datetime: Y-m-d\TH:i:sP (or Y-m-d). Passing a raw DB datetime string through unformatted is the most common validator failure.application/xml; charset=UTF-8 and add the Sitemap: https://example.com/sitemap.xml line to robots.txt. Neither the bundle nor the framework does this for you.StreamedResponse rather than concatenating one giant string — otherwise you'll hit memory_limit around the tens-of-thousands of URLs mark.var/logs/dev.log; a swallowed exception during template render produces an empty 200 response that looks like "no URLs." Also confirm the route is registered with bin/console router:debug (Symfony) before blaming the bundle.services.yml (Symfony 2 has no ergonomic service decoration) or subclass the controller. That's the supported escape hatch — expect to need it for anything beyond the bundle's defaults.How can I help you explore Laravel packages today?