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

Filament Redirects Laravel Package

asignua/filament-redirects

Asignua Filament Redirects is a Filament plugin for managing URL redirects directly from your admin panel. Create and edit 301/302 redirects, handle incoming requests with middleware, and keep your Laravel site's links organized without leaving Filament.

View on GitHub
Deep Wiki
Context7

Getting Started

  • Install the package and run its migrations (redirects, plus the 404 log table). Publish the config so config/redirects.php is in your repo — that file is the first place to look: gone_status (410 by default), preserve_query, and the redirect/gone status codes live there.
  • Register the resolver middleware (or whatever the package's route-level resolver is called) on the web group. It is the component that actually serves redirects and records misses into the 404 log, so without it nothing is captured.
  • First use case: pick one legacy URL, create a redirect through the model/repository or the panel, hit the old path, and confirm the browser lands on the new one. Then hit a URL that does not exist and confirm a row appears in the 404 log — that log is the intended day-to-day workflow ("Create redirect" straight from a miss).
  • Two classes are worth reading before you write any code: RedirectPath (the canonical normalize() / display() pair — everything you store goes through it) and RedirectRepository (the write-side API, including createFromLog()).
  • Run php artisan redirects:recheck once manually to see how the package re-validates targets; the panel's "Recheck" button is the same operation, scoped to recent rows.

Implementation Patterns

  • Never hand-write old_path. Always funnel through the normalizer. The package treats the stored value as canonical and the form value as "display", and v1.0.2 tightened the invariant so display() is the exact inverse of normalize(). If you build an import command, call the repository/normalizer rather than mass-assigning raw strings.
  • Creating redirects from the 404 log is already canonical. RedirectRepository::createFromLog() does not re-normalize, which is deliberate: log entries may contain https:/site.test/x or a language-prefixed en/foo. When you write your own admin action or Artisan command that consumes log rows, pass the log path through createFromLog() rather than normalize-then-create; double normalization was a real source of corrupt rows.
  • Wrap redirect writes in the host transaction. Model saved/deleted listeners and the repository use afterCommit to flush the cache, so it is safe (and encouraged) to create redirects inside your own DB::transaction(). Cache invalidation now happens once the outermost transaction commits; a redirect created mid-transaction will not be live until then, which is normally what you want.
  • Use the entity binding for content renames. Binding a redirect to a model means a slug/URL change is followed and a "Gone" target drops the binding automatically. Prefer Gone (served with gone_status, 410 by default) over deleting rows for retired content: it keeps the record, drops the stale binding, and returns the right status to crawlers.
  • Language-prefixed rows are addressed relative to their language. A redirect on an en row whose target is de/aktion is served as /de/aktion; the unprefixed language is reached by pasting the full address, which is kept whole and served from the root. Bulk "Create redirects" requires rows of a single language — chunk your import by language instead of mixing.
  • Preserve query strings deliberately. With preserve_query on, the incoming query is appended to the target — before any #fragment, since v1.0.2. If your targets already contain query strings, test the combination; appending to a target that already has a ? is the classic footgun.
  • Test with an assertion on Location, not on database state. Round-trip a few nasty sources through the form/save path (my page, what?, %3F, https:/site.test/x) and assert the served Location header. That exercises normalize → display → normalize in the same way the edit form does.

Gotchas and Tips

  • display() escapes scheme:/ on purpose. A source stored verbatim from the 404 log (https:/site.test/x, https:/evil.com/x) appears escaped in the edit form. That is not corruption — it is what makes the value survive a round-trip through the form. Do not "clean it up" in a custom view.
  • Untouched source fields are not re-normalized. RedirectPath::unchanged() guards this. If you build a custom edit form, replicate the "did the user actually change this field?" check, otherwise a prefixed source like en/foo gets normalized a second time and you will chase phantom redirect loops.
  • "Redirect loop detected" on an unrelated edit was caused by the to-path loop check using the submitted (rather than stored) source. Changing only the code or target on a language-prefixed row is fixed in v1.0.2 — if you see this on an older version, that is the bug, not your data.
  • Targets are not fully percent-decoded. %3F, %23 and %25 stay encoded, and an embedded query/fragment is left alone. If your redirect targets are built from user input, do not pre-decode them yourself; you will double-encode.
  • old_path is capped at 191 characters, checked on the normalized path in both the form and the repository. Long sources now raise a ValidationException rather than a SQL error — catch it in importers and report the offending row instead of aborting the batch.
  • Cache staleness is a transaction-ordering problem. All invalidation is afterCommit now; if you bypass the model (raw query builder inserts, DB::table('redirects')->insert()), you skip the listeners and leave a stale map. Route bulk imports through the repository or trigger a flush yourself.
  • Panel "Recheck" is bounded. It inspects the 2000 most recently seen 404-log rows, while redirects:recheck still walks the entire log. Use the command in a scheduled job and keep the panel for spot checks; do not expect the button to fix years of backlog.
  • The 404 log is intentionally not globally searchable. Its paths are not canonical, so searching them led to dead URLs. If you need log search, query the table directly or scope it to a language — do not re-enable a global search box in a custom resource.
  • Bulk "Create redirects" is per-language. Mixed-language selections are rejected because targets are relative to the row's language; split the operation rather than forcing it.
  • Extension points are the config file (gone_status, preserve_query, status codes), the model listeners for cache invalidation, and the repository's create methods. Prefer composing around RedirectRepository::createFromLog() over writing your own normalizer — the v1.0.2 fixes all landed in that boundary, and custom code at that seam is where regressions will appear.
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.
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
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk