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

Oc Sitemap Plugin Laravel Package

vdlp/oc-sitemap-plugin

Generate XML sitemaps for October CMS sites with this plugin, helping search engines discover and index your pages. Simple to install and configure, it keeps your sitemap current so your content ranks better.

View on GitHub
Deep Wiki
Context7

Getting Started

Set expectations first: this is an October/Winter CMS plugin, not a Laravel package. It is not installable into a vanilla Laravel app and exposes no standard config/ publishables or service provider you'd register by hand. If your project is October CMS (or Winter), everything below applies. If it isn't, stop — a framework-agnostic generator invoked from a scheduled job is the right tool.

The second expectation to set is that the repository is archived. The code works; nobody is going to fix it. Budget for vendoring or forking before you make it load-bearing.

Install — two steps, as always in October:

composer require vdlp/oc-sitemap-plugin
php artisan october:migrate
# if the plugin was already present and you're only refreshing its migrations:
php artisan plugin:refresh Vdlp.Sitemap

Then log into the backend and confirm the plugin appears under Settings. In October, plugin discovery is driven by the vendor namespace in composer.json plus the Plugin.php registration — if the settings page doesn't appear, the composer install didn't trigger October's autoload regeneration and php artisan october:util purge (or a re-run of the install) will fix it before you go looking for a config bug that isn't there.

First places to look, in order:

  1. Backend → Settings → Sitemap. This is the plugin's real UI and where you will spend most of your time. Sitemap items are defined here, not in code.
  2. GET /sitemap.xml on a running site. Confirm the route resolves before configuring anything — a 404 at this stage is a routing/plugin-registration problem, not a sitemap-content problem.
  3. plugins/vdlp/sitemap/Plugin.php and the plugin's config/ — for the enabled/disabled CMS-page behaviour and any per-site host settings. Read this before you file a bug about wrong URLs.

Lowest-friction first use case: add one CMS page reference for your homepage, add one model-based item pointing at an obvious model (RainLab\Blog\Models\Post, RainLab\Pages\Models\StaticPage), save, and fetch /sitemap.xml. If URLs appear, you already understand the whole integration model — everything after that is query tuning, cache invalidation, and multisite correctness.

Implementation Patterns

Reference content, not URLs. The plugin's entire value proposition is that entries are derived from CMS pages and Eloquent models rather than hardcoded. Define an item once and it tracks content changes. Any time you find yourself wanting to paste literal URLs into the sitemap, you are using the wrong abstraction — register a custom item type instead (see below).

Push publication rules into model scopes. For model-based items, attach a query scope that encodes your publication policy rather than relying on backend item defaults:

// Blog\Post
public function scopePublishedForSitemap($query)
{
    return $query->published()
        ->whereNotNull('published_at')
        ->where('published_at', '<=', now())
        ->withoutTrashed();
}

This is the single highest-leverage pattern in the package. Rules in PHP are reviewable, testable, and diffable; rules clicked into the backend settings table are none of those things.

Treat sitemap items as configuration, and make them reproducible. Items live in October's settings table — i.e. in the database, not in version control. That means a colleague's local install, your staging environment, and production can each have a different sitemap with no error and no warning. Mitigate deliberately: a small seeder, a plugin:refresh-adjacent migration, or a console command that reconciles items on deploy. Pick one and document it. This is the difference between a demo and an operable system.

Decide your regeneration trigger once. The three options have genuinely different failure modes:

  • Generate on request — fine for a few hundred URLs on a low-traffic site, fragile under load and under crawler bursts. Crawlers hitting /sitemap.xml are exactly the traffic pattern that makes this bad.
  • Generate on publish — model events or the backend form's afterSave hook. Fast, but you must catch every mutation path (save, delete, restore, bulk unpublish), which is easy to miss.
  • Generate on schedule — the safest default:
// Plugin::registerSchedule() or a dedicated command
$schedule->command('sitemap:generate')->hourly()->withoutOverlapping();

Whichever you choose, gate the output behind a cache key and clear that key on every mutation. A stale sitemap that still lists a deleted page is worse than a missing one, because it looks fine.

Multisite and multilingual. Configure host and locale explicitly per site so the generator emits correct absolute URLs and hreflang alternates. Test each site individually — the classic failure is a primary domain that's perfect and a second site whose sitemap points at the primary's host or drops locale prefixes. Add a test per site, not one test total.

Test the route, and test the negatives. This is the cheapest possible insurance:

public function test_sitemap_contains_only_published_posts()
{
    $published   = Post::factory()->published()->create();
    $draft       = Post::factory()->create(['is_published' => false]);
    $softDeleted = Post::factory()->published()->create(['deleted_at' => now()]);

    $this->get('/sitemap.xml')
        ->assertOk()
        ->assertSee($published->url())
        ->assertDontSee($draft->url())
        ->assertDontSee($softDeleted->url());
}

The negative assertions are the point. A sitemap that accidentally leaks drafts passes every positive test you'll write.

Extension point for anything that isn't a CMS page or a model. External API feeds, product catalogues synced from somewhere else, hand-rolled route collections — implement the plugin's sitemap item contract and register the type. Do not fork the plugin to add one-off URL sources; you'll be maintaining the fork against an archived upstream.

In local development, you usually want a representative sitemap without cloning production data. Seed a handful of published and unpublished records and assert against the route manually — this also validates your scope logic before you ever touch staging.

Gotchas and Tips

  • The repository is archived. This is the biggest single fact about the package. It works today against today's October, but it will not be updated for October core changes, PHP version bumps, or sitemap specification changes. Plan for: pin the exact version, vendor the source into your own namespace if it becomes load-bearing, and add a "does the sitemap still generate correctly?" check to your upgrade runbook. If you're choosing a package today for a long-lived site, weigh this heavily.

  • 2.4.0 has no changelog — only a compare link. Treat it as an unverified bump. Read the 2.3.0...2.4.0 diff, check plugin.yaml/composer.json for the October and PHP version floors, and re-run your sitemap feature test after composer update. For an SEO-critical output surface, "it's probably a patch release" is not sufficient due diligence.

  • A fresh environment silently produces an empty sitemap. Because items live in the settings table, october:migrate on a clean database gives you a valid but nearly empty /sitemap.xml with no error anywhere. Symptom: route returns 200, you see one or two URLs, you assume the plugin is broken. Check the backend item list first — it's almost always empty.

  • Absolute URLs come from the October site definition, not APP_URL. When hosts in the sitemap are wrong, the fix is in the backend Sites panel, not in .env and not in the plugin. Verify the site's host and locale config before you go spelunking.

  • noindex leakage is the most common correctness bug. Model items that don't filter drafts, soft-deleted rows, or pages flagged noindex will publish them to crawlers. The plugin will not infer your intent. Filter in the scope, assert in tests.

  • Large models will time out. An unbounded query over tens of thousands of rows blows memory and generation time. Constrain by a published_at window, chunk where the item type supports it, and move generation to a scheduled job rather than firing it on every editor save.

  • Mind the 50,000-URL / 50MB protocol limit. At scale you need a sitemap index with child files. Check whether this plugin supports splitting before you assume it does; if your catalogue is going to cross that line, that's a design constraint worth knowing about at evaluation time, not at go-live.

  • Decide trailing-slash policy once. Mismatch between sitemap URLs and your page canonicals generates Search Console warnings that look like a plugin bug but are a routing/canonical convention problem. Same for paginated archives: exclude them unless you deliberately intend to list them.

  • Debug a missing URL in php artisan tinker first. Nine times out of ten the item definition is correct and the scope returns zero rows — wrong published_at, a locale filter, a soft-delete, or a site restriction. Reproduce the query directly before you suspect the generator.

  • Check the sitemap with real content, not fixtures. The failures that matter are content-shaped: wrong hosts on the second site, drafts leaking, 404s on redirected URLs, malformed <lastmod>. A staging environment with production-like data plus a diff of pre/post-deploy sitemap.xml catches nearly all of them.

  • Add a cheap external check. Scheduled fetch of the live /sitemap.xml, validate the XML, assert on entry count and status codes, alert on discontinuity. Sitemap breakage is invisible to infrastructure monitoring but very visible to the business — a URL-count drop is the alert you actually want.

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.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console