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

Sitemap Xml Bundle Laravel Package

evheniy/sitemap-xml-bundle

SitemapXmlBundle is a Symfony2 bundle that generates sitemap.xml files for your site. Install it via Composer, register it in AppKernel before your main bundle, then extend SiteMapDumpCommand and implement setEntities() to define your domains and page URLs.

View on GitHub
Deep Wiki
Context7

Getting Started

First, confirm you can actually run it. This is a Symfony2 bundle, not a Laravel package — there is no service provider, no config file to publish, and no facade. If your app is Laravel, you cannot composer require your way to a working sitemap; the realistic path is to read the entity classes (they are thin fluent builders) and port the XML-serialisation shape into an Artisan command. Everything below assumes either a Symfony 2.x app, or a Laravel developer mining the package for its output contract.

Minimal steps (Symfony2):

  1. composer require evheniy/sitemap-xml-bundle "1.*" — pin the major, because the last tagged release is from October 2015 and nothing upstream will ever fix a regression for you.
  2. Register the bundle in AppKernel::registerBundles(), before your own bundle (new Evheniy\SitemapXmlBundle\SitemapXmlBundle() above new AppBundle\AppBundle()). The README calls this out explicitly; the ordering is what lets your subclass of the dump command win over the bundle's.
  3. Create AppBundle\Command\SiteMapDumpCommand extending Evheniy\SitemapXmlBundle\Command\SiteMapDumpCommand, and override setEntities().
  4. Set the domain on the dump entity ($this->dumpEntity->setDomain('site.com')) and attach locations to a site map entity.
  5. Run app/console sitemap:dump.

Where to look first, in order:

  • Resources/docs/service_manager.md — the $this->serviceManager->create*Entity() factory is the only construction path you should use; skipping it means you bypass whatever wiring the bundle does.
  • Resources/docs/dump_manager.md and dump_entity.md — these tell you where the file lands and how the domain is applied. The README never states the output path, so read this before wiring a web route or a cron.
  • sitemap_entity.md / location_entity.md — the core addLocation(...setLocation(...)->setLastmod(...)) shape you will use 95% of the time.
  • image_entity.md, video_entity.md, news_entity.md — only if you have those media types; each carries required fields Google validates.

First use case — make it the boring one. Pull a list of canonical URLs and updated_at timestamps out of your ORM, loop, and emit a flat <urlset>. Do not start with a sitemap index, hreflang, or news; get one valid file validating in Google Search Console first.

protected function setEntities()
{
    $this->siteMapEntity = $this->serviceManager->createSiteMapEntity();
    $this->dumpEntity->setDomain('example.com');

    foreach ($this->pageRepository->findIndexable() as $page) {
        $this->siteMapEntity->addLocation(
            $this->serviceManager->createLocationEntity()
                ->setLocation($page->getCanonicalUrl())
                ->setLastmod($page->getUpdatedAt())
        );
    }
}

Implementation Patterns

Command-as-adapter, entities-as-DTOs. The pattern the package pushes you toward is: your setEntities() override is the only application-specific code, and everything it does is compose entities from the service manager. Keep repository queries, filtering, and business rules in your own services and inject them into the command; setEntities() should read like a translation layer, not like a data-access layer.

Fluent composition beats intermediate variables. The documented style is a single nested expression — index → site map(s) → location(s) → image(s). It is readable for small trees but becomes unmanageable past a dozen entries; when a location needs several child entities, assign the location to a variable, add the children to it, then addLocation(). Same output, far easier to debug.

Sitemap index as the scaling switch. The README's threshold is 50,000 links. The structural difference is small: swap createSiteMapEntity() for createSiteMapIndexEntity() at the root and addSiteMap() each site map instead of addLocation() each page. Because both root entities share the same fluent shape, build a helper that chunks your URL list into N-sized site maps and feed the chunker either mode — that keeps the 50k cutover a one-line change rather than a rewrite.

Regenerate on write, not on read. Nothing in this package invalidates anything. The durable pattern is a deploy hook or a queued job on content publish that runs sitemap:dump, with the web route serving the pre-built file. Per-request generation from a controller is a trap: you pay full enumeration cost on every crawl and inherit the 50k ceiling at runtime.

Segment deliberately. Multiple site maps (products, blog, static pages) mapped one-to-one onto your content types makes partial regeneration cheap — republish a blog post, rebuild only the blog site map — and makes it obvious which content class is missing from the index when crawl coverage drops.

Laravel adaptation. Port the shape, not the code: a SitemapService that streams <urlset> with XMLWriter (cheap on memory for large sets), an Artisan command wrapping it, and a route returning the file with Content-Type: application/xml. You already have the URL source in Eloquent, which is the part the bundle was abstracting away.

Gotchas and Tips

  • Bundle registration order is load-bearing. The README says to register SitemapXmlBundle before your main bundle. Get it wrong and your SiteMapDumpCommand subclass is silently shadowed by the base command — you will see the base behaviour with no error, which is a miserable hour to debug.
  • setDomain() goes on $this->dumpEntity, not the site map entity. It is easy to misplace because the site map entity is what you are actively building. Read the README example literally; the domain lives on the dump entity.
  • No configuration tree. There is no config.yml section to tune. Everything is driven from setEntities() and the DumpManager. If you need a different output path or filename, look at dump_manager.md — and expect to subclass rather than configure.
  • The 50,000 link limit is yours to enforce. The bundle will happily emit a site map beyond the protocol limit and Google will ignore or truncate it. Add an assertion or a chunking guard in setEntities() so the failure is loud at build time, not quiet at crawl time.
  • Media entities have required fields. Image, video, and news extensions each need more than a location — video in particular needs a title, description, thumbnail, and publication date to validate. Use the per-entity docs, and validate the generated file against the schema in CI; a malformed entry can invalidate the whole <urlset>, not just that one row.
  • Date handling. setLastmod(new \DateTime($page['date'])) is the documented form — pass a DateTimeInterface produced from real content timestamps, not new \DateTime() per row. A sitemap where every lastmod equals build time trains crawlers to distrust your dates, which is worse than omitting the field.
  • PHP compatibility is unverified. A 2015 release predates modern PHP. Before you build on it, smoke-test the command on your actual runtime; expect to patch deprecations or dynamic-property warnings yourself, and vendor the forked copy under a repositories entry if you do.
  • Symfony 4+ has no bundles. If a framework upgrade is anywhere on the roadmap, this package is a removal item, not a migration item. Treat the setEntities() body as the thing worth keeping — it is portable — and the bundle wiring as disposable.
  • Extension points, ranked by usefulness: (1) subclass the dump command and inject services, (2) compose entities in different trees for segmented output, (3) write your own entity class if you need a namespace the bundle does not cover — but check the fluent-builder contract in an existing entity first, since it is undocumented outside the source.
  • Put it behind a test. One test asserting the generated XML parses and matches the sitemap schema is the highest-value hour you will spend on this dependency. With 1 star, 0 dependents, and no release since 2015, that test is your only safety net.
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/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
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route