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

Symfony Route Usage Laravel Package

migrify/symfony-route-usage

Symfony Route Usage scans your Symfony app to find routes that are never referenced anywhere in your code, helping you spot dead endpoints and clean up routing config. It reports unused routes so you can safely remove them, keeping your route definitions lean and your application easier to maintain.

View on GitHub
Deep Wiki
Context7

Getting Started

Before you install anything, read the repo name. This package lives under deprecated-packages/ — it is archived and unmaintained. Treat it as a design reference and a short-lived audit tool, not a permanent dependency. Pin the exact version and expect to own the code eventually.

Minimal setup:

composer require migrify/symfony-route-usage
  1. Register the bundle in config/bundles.php (it is a Symfony bundle, not a plain library).
  2. Provide Doctrine ORM. The package persists route hits — it needs a RouteUsage-style entity/table (route name, hit count, last-used timestamp). Doctrine ORM + a working DB connection is a hard prerequisite; there is no filesystem or in-memory fallback.
  3. Create the table. Update your Doctrine schema or generate a migration for the usage table, then run it.
  4. Enable the request listener so hits are recorded on kernel.request.
  5. Inspect results with the console command the bundle ships (something like bin/console route:usage) — it prints routes that have never been hit versus those with recorded traffic.

Where to look first: vendor/migrify/symfony-route-usage/src/ for the event subscriber and the console command. The listener is the piece you will end up customising; the command is the piece you will end up replacing with your own SQL.

First use case: point it at a legacy application, let it collect for a sprint or two, then use the "never used / not used in 90 days" list to prioritise route removal and dead-controller cleanup. This is genuinely useful before a big refactor or a framework upgrade — it turns "I think this is dead" into a data-backed list.

Implementation Patterns

Run the recorder only where traffic is real. Enabling the listener in dev and test pollutes the table with your own clicking and with functional-test requests, making every route look used. Gate it to prod (and maybe staging) via the bundle config or a firewall/env check.

Filter aggressively before you trust the output. The first run will always show noise. Exclude, at minimum:

  • health checks, readiness probes, uptime monitors
  • _profiler, _wdt, _error, _preview_error, asset/thumbnail controllers
  • webhooks and payment callbacks (they are used, just rarely, and by machines)
  • admin/back-office routes if only a handful of staff touch them
  • routes reached only after a redirect, which may register a different name than you expect

Most implementations of this package expose a route-name exclusion list; if not, decorate the subscriber.

Segment before you conclude. A single global "last used" column lies in multi-app setups: an API route exercised only by an external client looks dead if you only browsed the web UI. If you share the usage table across apps/environments, add an application or host discriminator, otherwise aggregate per database.

Turn it into a CI gate, carefully. A useful pattern is a scheduled job (weekly) that queries the usage table for last_used_at < NOW() - INTERVAL 90 DAY and posts the list to Slack or opens a ticket. Do not make it fail the build on day one — you will spend the first month arguing about false positives. Whitelist explicitly, then ratchet.

Backfill instead of waiting. If you have nginx/Apache access logs, parse them into the usage table rather than waiting weeks for the listener to accumulate history. This is often faster and more accurate than live recording, and it lets you evaluate the package's value in an afternoon.

Pair it with the router. bin/console debug:router --show-controllers gives you the authoritative route list; the usage table only tells you which of those were hit. Diff the two, and cross-reference with grep over templates, JS, and email templates for hard-coded paths — a route with zero hits and zero references is a safe delete; zero hits with references means your traffic sampling is wrong.

Custom repository queries beat the bundled command. Add your own RouteUsageRepository::findUnusedSince(\DateTimeInterface $since) and build whatever report your team actually reads. The shipped console output is a starting point, not a deliverable.

Gotchas and Tips

Maintenance is the headline risk. Archived package, zero dependents, effectively zero stars. Expect no compatibility work for modern Symfony/PHP. If composer require fights your Symfony version, do not patch the vendor directory — copy the ~150 lines of listener + entity into your own src/ and move on. That is roughly the entire value of the package.

Write amplification on every request. Recording a hit means a DB write (or at least an UPDATE) per request. On a busy app, that is a real cost and a real source of lock contention on a single row per route. Mitigations: only update when the day changes, sample (e.g. 1 in N requests), push to a queue, or log to a file/Redis and aggregate nightly. Never let a failure in the recorder break the request — wrap it so exceptions are swallowed or logged, not propagated.

Sub-requests and fragments double-count. ESI, render(controller(...)), and forwarded requests fire kernel.request again. You will see inflated counts and occasionally routes "used" only because a template fragment rendered them.

Non-HTTP usage is invisible. Console commands, Messenger workers, and cron jobs that call services directly never touch a route, so a controller that is only invoked internally will look dead. Conversely, routes used exclusively by scheduled HTTP self-calls will look used when they are actually redundant.

Unnamed routes and route collisions. Routes without an explicit name get auto-generated names that can shift between versions. Match on name, but sanity-check against the path before deleting anything.

Cache and warmup. If the listener is registered lazily or after the router cache is warmed, you can see an empty table in production for longer than expected. Clear and warm the cache after enabling, then verify a single hit lands in the table before trusting a "0 hits" report.

"Unused" ≠ "safe to delete." External consumers, links in already-sent emails, QR codes, printed URLs, and partner integrations all bypass your instrumentation. For anything publicly reachable, prefer returning 410 Gone with a redirect for a release cycle before removing the route outright.

Extension points: the request subscriber (decorate to filter, sample, or make writes async), the usage entity/repository (add columns like first_used_at, app, host), and the console command (replace entirely). Everything else is thin glue.

Verdict for day-to-day use: worth a two-week experiment on a legacy app with a Doctrine-backed stack and a pinned Symfony version. Not worth adopting as infrastructure. For anything new, write the listener yourself — it is small, you control the schema, and it will still work after the next Symfony major.

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