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

Laravel Model Permalink Laravel Package

open-southeners/laravel-model-permalink

Laravel package that generates permanent, shareable links for Eloquent models. Create stable permalink URLs for any record with configurable routes and slugs, so entries stay reachable as their content or IDs change.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install with composer require opensoutheners/laravel-model-permalink, then publish and run the migration so the permalinks table exists (php artisan vendor:publish --tag="model-permalink-migrations" and php artisan migrate). Publish the config file as well if you need to change the route prefix, middleware, or redirect behaviour.
  • Look first at config/model-permalink.php (route registration/prefix/middleware), the HasPermalinks trait, and the package's own controller — that controller is the code path that resolves an incoming permalink and redirects to the model, and it is now also the source of the new PermalinkGotAccessed event.
  • Add the trait to any model you want to expose via a short, shareable link:
use OpenSoutheners\LaravelModelPermalink\Concerns\HasPermalinks;

class Invoice extends Model
{
    use HasPermalinks;
}
  • First use case: create a permalink for a record and hand the URL out instead of the canonical route (receipts, invoices, shared documents, invite links, public profile pages). Visiting the permalink hits the package controller, which resolves the model and redirects — with 1.1.0 you can now hook into that moment:
use OpenSoutheners\LaravelModelPermalink\Events\PermalinkGotAccessed;

Event::listen(function (PermalinkGotAccessed $event) {
    // $event->permalink (and/or the related model) — inspect the class for the exact payload
    AccessLog::create([...]);
});

Implementation Patterns

  • Create on model creation, not in controllers. Generate the permalink in a created model event or an observer so every record is shareable regardless of which entry point created it. Controllers stay thin and you avoid the "some rows have no permalink" data drift.
  • Treat the permalink as an identifier, not a slug. Permalinks are opaque tokens by design — don't encode sequential IDs or user-controlled strings if the resource is publicly reachable, otherwise you reintroduce enumeration. Keep the canonical slug/SEO URL separate.
  • Access tracking via the new event. PermalinkGotAccessed is the clean integration point for analytics: store hits, increment counters, feed a dashboard, or dispatch a queued job. Keep the listener fast and ShouldQueue any I/O so a slow analytics write never delays the redirect.
  • Attach metadata to the event, not a second query. If your listener needs the model type or user context, read it from the event payload (permalink → morph relation) rather than re-resolving the permalink yourself; re-resolving duplicates the package's work and can be inconsistent with what was actually served.
  • Middleware in config, not in your routes. Gate who can access permalinks (auth, signed, rate limiting) via the published config so the package route inherits it. This keeps the redirect path consistent for every model type.
  • Custom resolution when you need control. If you need to branch on payload (expired link → 410, revoked → 403), override the package controller/route rather than fighting the default redirect. Just remember that once you leave the package controller, the new event stops firing — see Gotchas.
  • Testing. Assert the redirect target rather than the permalink string, and use Event::fake([PermalinkGotAccessed::class]) plus Event::assertDispatched to verify your tracking wiring independently of the redirect assertion.

Gotchas and Tips

  • The event only fires through the package's own controller. This is the single biggest trap in 1.1.0. Any resolution you do yourself — a custom route, resolving a permalink in a Blade view, a QR-code pre-check endpoint, a CLI/integration test that hits the model directly — will not dispatch PermalinkGotAccessed. If you need guaranteed coverage, either funnel all access through the package route or add your own explicit event dispatch at the other entry points; don't assume the listener sees everything.
  • Check the event's public API before depending on it. Confirm the exact property names (permalink instance vs. model vs. request) against src/Events/PermalinkGotAccessed.php. Newly added events often get refined in the next minor, so wrap access in your listener and keep the listener thin.
  • Don't do write-heavy work synchronously in the listener. A counter increment is fine; a write to a remote analytics service is not — it sits directly in the redirect path.
  • Routing conflicts. A catch-all permalink route will swallow other top-level routes if registered early or if the prefix is /. Use a distinct prefix (e.g. /p/{permalink}) or verify route order; a permalink that collides with a real path silently shadows it.
  • Lifetime and revocation semantics. Deciding between 404 (never existed) and 410 (existed, revoked) matters for crawlers and for anyone who bookmarked a link. Handle deleted/expired permalinks deliberately, including soft-deleted parent models — the redirect target may no longer resolve even though the permalink row does.
  • Debugging misses. If a permalink 404s, check in this order: migration ran, the trait is on the model, the permalink row exists and points at an existing model, then the route middleware (auth redirects look like failures but aren't). php artisan route:list --path=<prefix> confirms the package route is actually registered.
  • Extension point to watch. The event is also a good seam for cache warming or link-preview generation, but it fires per-access, so debounce or dedupe by permalink if the downstream work is expensive.
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.
hsluv/hsluv
seampact/codegen
seampact/hooks
seampact/ir
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support