goldnead/statamic-smartlinks
Statamic addon for smart internal links: reference entries, pages, or URLs with simple syntax and let the addon resolve them into valid links automatically, so content stays intact even when slugs or site structure change.
Scope note: no prior assessment exists and no repository/telemetry data was supplied; this evaluation is derived solely from the v0.2.1 release notes and must be re-validated against the codebase.
Architecture fit. The package owns the full smartlink lifecycle inside the Laravel application: collections (songs, releases, likely artist/other), per-platform stored links, a health-check history, configurable route segments (smartlinks.routes.segments), a console command (smartlinks:resolve), and URL builders (landingUrl(), clickUrl()) consumed by tags and a "CP build" (static/control-panel output). The fact that URL construction is centralised in two methods used consistently by tags and the build is the strongest architectural signal here — it means the route change is contained to a small number of call sites rather than scattered through templates. Config-driven per-collection segments are the right abstraction for a slug-collision problem and scale to future collections without further code changes. This is a small, opinionated, single-purpose package: low architectural surface, high coupling to its own config and command schedule.
Integration feasibility. Straightforward for a Laravel shop: composer install, publish/merge config, register routes, schedule smartlinks:resolve. The v0.2.1 work does not add new infrastructure dependencies. The only integration friction is URL-related, not code-related.
Technical risk.
/hoeren/release/{slug} while song URLs are unchanged. Any external inbound link, sitemap entry, QR code, ad campaign, or persisted absolute URL stored in the database or baked into a previously generated CP build will 404 after deploy. No redirect or backfill is mentioned.--replace-dead writes the newly found link into the dead link's row, drops that platform's check history, and preserves other columns. That is an irreversible audit-trail loss and an in-place mutation with no stated dry-run, transaction boundary, or rollback.smartlinks:resolve: it now counts every stored platform as present whether dead or not, and no longer appends a duplicate next to a hidden dead link. Any downstream consumer counting rows or expecting the previous append-on-resolve pattern will see different results without any error.Key questions.
--replace-dead transactional, idempotent, and runnable as a dry-run? Where do the replacement logs go and what is their retention?smartlinks.routes.segments identical across all environments and is it covered by a deploy-time check?route:cache) part of the deploy pipeline?Stack fit. Assumes a standard Laravel app with config publishing, route registration via a service provider, an artisan command on the scheduler, and a build step ("CP build") that consumes package-generated URLs. Nothing in v0.2.1 changes that shape; the package remains a drop-in composer dependency.
Migration path. This release requires an explicit migration, not a composer update:
smartlinks.routes.segments map (song segment unchanged, release segment new)./hoeren/release/{slug} at the web-server or route level.Compatibility. Song URLs are explicitly unchanged, which bounds the blast radius to releases. suspect/unknown links are explicitly never touched by --replace-dead, which is a sensible safety boundary. The unconditional change to smartlinks:resolve counting semantics is the main compatibility caveat and has no opt-out flag — verify every consumer of the link table before enabling.
Sequencing. Deploy code + config → clear and rebuild route caches → verify new URL shapes via landingUrl()/clickUrl() smoke tests → land redirects before traffic hits the new build → regenerate CP artifacts and tags → run --replace-dead manually in a low-traffic window with a DB snapshot → only then add the flag to the scheduled run. Redirects and artifact regeneration must not be deferred to a follow-up ticket.
Maintenance. Low ongoing code maintenance; the recurring cost is configuration ownership. smartlinks.routes.segments and its collection ordering become production-critical config that must be reviewed like code and diffed across environments. Budget for a documented runbook covering the URL scheme and the dead-link replacement policy.
Support. Expect a short-term spike in inbound 404 reports, broken-campaign reports, and SEO/analytics anomalies immediately after deploy. Content ops will need a one-page explainer on the new release URL shape and on why some platform links get replaced. The replacement log is the first place support will look — make sure it is queryable and retained.
Scaling. smartlinks:resolve now processes every stored platform each run regardless of dead status, so per-run work grows monotonically with catalogue size rather than with the number of dead links. This is fine today but should be watched: runtime, outbound request volume, and third-party platform rate limits are the likely ceilings. If the catalogue grows, expect to need batching or filtering flags in a later release.
Failure modes.
segments collection ordering is misconfigured — the lookup succeeds silently against the wrong collection rather than erroring.--replace-dead dropping check history with no stated rollback; a bad replacement also silently overwrites a row while keeping unrelated columns, so the record can look internally inconsistent.smartlinks:resolve for any dashboard or export that counts stored platforms.Ramp-up. Low for a Laravel developer — one config file, one command, two URL helpers. Medium for content/ops staff, who must absorb both the new URL scheme and the dead-link replacement semantics. Recommend a half-day of engineering time for the migration and a short written handover for ops before the release is promoted beyond staging.
How can I help you explore Laravel packages today?