dragon-code/laravel-route-names
Automatically generates names for your Laravel routes based on controller classes and methods, so you never name routes by hand. Keeps naming consistent across your app, makes refactoring safer, and works alongside your existing route definitions.
composer require dragon-code/laravel-route-names
The package ships with Laravel package discovery, so there is normally no manual config/app.php edit. Publish its config before doing anything else:
php artisan vendor:publish --tag=route-names
# or, if the tag differs:
php artisan vendor:publish # then pick the DragonCode entry from the list
Where to look first
config/route-names.php — the only knobs that matter (exclusions, prefixes, controller/namespace handling). Read it top to bottom; it is short.src/ServiceProvider.php and whatever class hooks the router — the package is small enough to read in one sitting, and the source is the real specification.php artisan route:list — the ground truth for what names were generated.First use case
Take one controller with a handful of plain routes, strip the ->name() calls, and diff the route table:
// before
Route::get('/users', [UserController::class, 'index'])->name('users.index');
Route::get('/users/{user}', [UserController::class, 'show'])->name('users.show');
// after
Route::get('/users', [UserController::class, 'index']);
Route::get('/users/{user}', [UserController::class, 'show']);
php artisan route:list --json > after.json
If you get users.index / users.show back, the convention holds and you can roll it out. If not, stop and read the config before touching more files — do not migrate the whole app on faith.
Convention over configuration for controller routes. The core workflow is: register routes with array-callable controllers, drop explicit names, and let the package derive {controller}.{method} from the class and action. This is the highest-value use of the package — it removes a whole class of drift where a route is renamed or moved but its ->name() lags behind.
Invokable controllers. Single-action controllers have no method segment to derive from, so expect the name to come from the controller class alone. Verify one before relying on it; this is the most common place for surprises.
Let explicit names win. Keep ->name() on any route whose public name is a contract — URLs you email, routes referenced by third-party packages, anything in a signed URL. The package fills in the blanks; it should not be treated as authoritative over an explicit name you set. Test this assumption once early (set an explicit name on a controller route and confirm it survives) so you know the precedence order in your version.
Resource routes are mostly already named. Route::resource() produces users.index, users.show, etc. on its own. The package's value there is the extra routes you bolt onto a resource — export endpoints, bulk actions, nested children — where you would otherwise hand-write a name that drifts from the resource's naming.
Group prefixes. If name generation ignores ->name('admin.') group prefixes, an admin route and a public route on the same controller action will collide. Audit every group that has a name prefix before trusting the generated names, and consider using ->as() deliberately on those groups to keep the prefix in the pipeline.
CI route-name snapshot. Because names are now implicit, a controller rename becomes a silent breaking change. Add a cheap guard:
php artisan route:list --json | jq -S 'sort_by(.name) | map({name, uri, action})' > routes.lock
git diff --exit-code routes.lock
Commit the lock file. Any refactor that changes a public route name now fails CI instead of failing in production.
Blade / Livewire / Inertia. Nothing changes at the call site — route('users.show', $user) keeps working as long as generation matches what you expected. The migration is therefore incremental by controller, which is the right rollout unit.
Testing. assertRouteIs() and URL::route() behave identically, so the risk is concentrated in string literals — redirects written as redirect()->route('users.index') will only fail at runtime. Grep for route(' and to_route(' before removing any hand-written name.
Maintenance status: alive, but solo. As of 2.2.0 the package is actively maintained — it picked up Laravel 13 support via a community PR (#58), which tells you two things: the maintainer is responsive to framework-compatibility PRs, and the compatibility work is being contributed rather than driven internally. Budget for that: when a new Laravel major lands, expect a gap before a constraint bump appears, and be ready to pin or carry a fork for the interval. Do not let a stalled release block your framework upgrade.
Pin to 2.2.0 on Laravel 13. The Laravel 13 compatibility constraint arrived in 2.2.0. Anything older will refuse to install or resolve against a Laravel 13 illuminate/support. If you are on Laravel 13, ^2.2 is the floor, and you should confirm the resolved version in composer.lock rather than trusting the caret.
Duplicate names fail silently. Laravel does not throw when two routes share a name; the last registered one wins the reverse lookup, and route() starts resolving to the wrong URI with no error. Auto-naming makes collisions far easier to create (same controller action reachable at two URIs, or grouped prefixes that get dropped). Run a duplicate check as part of the snapshot step:
php artisan route:list --json | jq -r '.[].name' | sort | uniq -d
Closure routes and vendor routes. Closure-based routes have no controller to derive from and will be left unnamed — fine, but they will not appear in your generated set. More dangerously, any package that registers controller routes through the same router may pick up generated names. If a third-party package does route('vendor-package.index') and its route got renamed, you get a RouteNotFoundException at runtime. Check config/route-names.php for exclusions and register the package's controllers there.
Cached routes and cached config. Names are generated at registration time, so php artisan route:cache freezes them. If you edit config or add exclusions, you must route:clear + config:clear before the change is visible — otherwise you will debug a phantom. After a version bump, always clear both; a stale route cache can mask a generation-behaviour change between minor versions just as easily as a config change.
Debugging order of operations. When a name is not what you expect: route:clear, route:list --path=your/uri, then read the resolver class. Do not start by reading the README — it is thin, and the source is the actual specification.
The fallback route. Route::fallback() has no name and no controller. Make sure your generated set does not accidentally claim the name you use for it.
Signed URLs depend on names. URL::signedRoute('name') and URL::temporarySignedRoute('name') resolve through the name registry. Any name change invalidates in-flight signed links. If you use signed URLs for email verification or downloads, pin those routes with explicit names and exclude them from the migration.
Extension points are shallow. Beyond the config file, there is unlikely to be a clean resolver interface to swap. If your naming convention differs from the package's (e.g. you want module-prefixed names like billing.invoices.index), you are better off writing a small Route::macro or a service provider loop that applies ->name() after the fact than fighting the package's logic.
Net recommendation: 2.2.0 removes the strongest objection to this package — the Laravel 13 constraint is current, so it is a viable dependency on a modern stack rather than a stranded one. That said, the structural picture is unchanged: single maintainer, low adoption, and the package sits directly in the route registration path. Treat it as a thin convention layer you could own: adopt it, but read the resolver once and keep the CI route-name snapshot in place so you can lift the logic into App\Support without pain if maintenance lags. For a package this small, vendoring the router hook is still often the better long-term call — you keep the convention and drop the dependency risk.
How can I help you explore Laravel packages today?