Open-source project
spatie/laravel-route-attributes avatar
spatie/laravel-route-attributes

laravel-route-attributes: registering Laravel routes with PHP 8 attributes

Use PHP 8 attributes to register routes in a Laravel app

883 stars85 forksPHPMIT

At a glance

What is it?
The spatie/laravel-route-attributes package lets you declare a Laravel route as an attribute on a controller method instead of a line in a routes file, and it scans your controller directories for them. The interesting parts are the per-directory group configuration, the pattern filters for co-located tests, and the resource controller attributes with their many named parameters.
Who is it for?
laravel-route-attributes suits a Laravel team that wants its route definitions next to the controller methods they belong to, and that is comfortable letting a package scan the source tree to find them. It does not suit a codebase that keeps all routes in one explicit file on purpose, since the package trades that visibility for locality, and it uses PHP attributes; check the selected package version's PHP and Laravel requirements before installing.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 2 days ago.
What is it written in?
Mainly PHP, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on November 7, 2026, and from our analysis. They are not legal advice.

From daily snapshots

Star history

  • spatie/laravel-route-attributes883 ★ · +0
Star history
Repository2026-10-222026-11-07
spatie/laravel-route-attributes883883

0 Stars in the last 16 days, measured from the daily GitHub snapshot. Compare with other projects

Editorial analysis

The route lives next to the method, and the file is scanned for you

The package provides attributes to automatically register routes, and the shortest example is a Get attribute on a controller method:

php
use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route')]
    public function myMethod()
    {
    }
}

That attribute automatically registers the route you would otherwise have written by hand in a routes file:

php
Route::get('my-route', [MyController::class, 'myMethod']);

The trade is explicit. You give up a single file where every route is visible at once, and you get the route definition in the same file as the code it points at, which is where a reader is already looking when they wonder what URL this action answers. The registration is not something you list, it is something the package discovers. The annotation style works because PHP 8 attributes are attached to the method in the source, so a scanner can read them at boot time and turn them into route registrations, and the package has a video that walks through both the attributes and how the package works under the hood.

Installing it and publishing the config

The package is installed with composer:

bash
composer require spatie/laravel-route-attributes

The configuration file is published with an artisan command that names the service provider and the config tag:

bash
php artisan vendor:publish --provider="Spatie\RouteAttributes\RouteAttributesServiceProvider" --tag="config"

The published file starts with the switch that turns the whole mechanism on or off, since automatic registration of routes will only happen if this setting is true. It then lists the directories to scan, and this is where the design becomes interesting, because a directory entry can carry group configuration. The current default configuration scans the controllers directory and applies SubstituteBindings middleware. Its API directory group is a commented example; configure prefixes and middleware explicitly for the groups your application needs. So the grouping is not something you repeat on every controller, it is a property of where the controller lives. That is the part of the design that makes the package work on a large codebase rather than just on a demo.

Directories can carry a namespace, and patterns can exclude your tests

For a codebase that does not keep controllers in the application root namespace, the directories array accepts a namespace to path pattern, and the example shows controllers from Modules\Admin\Http\Controllers being included. It works in both directions, either the namespace maps to a path:

php
'directories' => [
    'Modules\Admin\Http\Controllers\\' => base_path('admin-module/Http/Controllers'),
    // Or
    base_path('admin-module/Http/Controllers') => [
        'namespace' => 'Modules\Admin\Http\Controllers\\'
    ],
    app_path('Http/Controllers'),
],

or a path maps to a namespace, and both can be listed alongside the ordinary application path. There is a second problem the configuration also solves. If you co-locate multiple kinds of file in the same directory, the package would otherwise scan everything in it, so the patterns and not_patterns options let you narrow the scan. The example registers only files matching a controller pattern and excludes test files:

php
'directories' => [
    base_path('app-modules/Blog') => [
        // only register routes in files that match the patterns
        'patterns' => ['*Controller.php'],
        // do not register routes in files that match the patterns
        'not_patterns' => ['*Test.php'],
    ],
],

The `not_patterns` comment matches the option's purpose: files matching those patterns are excluded from route discovery.

Every HTTP verb has an attribute, and they stack on a method

The package provides several annotations to put on controller classes and methods, and the verb coverage is complete, which the README puts mildly. You can use these attributes on controller methods, and the example shows them stacked, which is the interesting part since each one adds a route rather than replacing the others:

php
#[Spatie\RouteAttributes\Attributes\Post('my-uri')]
#[Spatie\RouteAttributes\Attributes\Put('my-uri')]
#[Spatie\RouteAttributes\Attributes\Patch('my-uri')]
#[Spatie\RouteAttributes\Attributes\Delete('my-uri')]
#[Spatie\RouteAttributes\Attributes\Options('my-uri')]

The attributes can go on the class as well as the method, which is what makes the resource controller case work, since a resource attribute describes a set of routes for the whole controller rather than one action. The package name in the vendor namespace is spatie/laravel-route-attributes, the licence is MIT, and check the project's release history and compatibility constraints for the version you plan to install.

Resource controllers with named parameters for everything

Registering a resource controller uses the Resource attribute, and the example shows how many knobs it has. You can use only or except to control which resource routes are available, the parameters parameter modifies the default parameters the resource attribute sets, and the names parameter sets the route names, accepting a string for a base name for each action or an array to name each action individually. The shallow parameter makes a nested resource apply nesting only to the routes without a unique child identifier, which is index, create and store. There is an apiResource boolean parameter to include only the actions used in APIs, and an ApiResource attribute that extends the Resource attribute class with that parameter already set to true. The example combines a Prefix attribute with a Resource attribute:

php
use Spatie\RouteAttributes\Attributes\Resource;

#[Prefix('api/v1')]
#[Resource(
    resource: 'photos.comments',
    apiResource: true,
    shallow: true,
    parameters: ['comments' => 'comment:uuid'],
    names: 'api.v1.photoComments',
    except: ['destroy'],
)]
// OR #[ApiResource(resource: 'photos.comments', shallow: true, ...)]
class PhotoCommentController
{

The parameters array is the part with a real consequence, since mapping comments to a comment:uuid binding changes what the model lookup in the action receives, and the except list removes the destroy route entirely rather than hiding it in the controller.

The package is a discovery mechanism, so the source tree is the configuration

The thing to keep in mind when you adopt a package like this is that it turns your directory structure into configuration. Every controller directory becomes a route group, its prefix and middleware come from the configuration, and the scan itself is what registers the routes. That has two consequences. The first is that a refactor of your directory layout is a change to your routing, which is a good thing if you wanted the coupling and a surprising thing if you did not. The second is that the tool is invisible at the point of use, since the route file no longer exists, so a new developer looking for the URL of an action has to open the controller rather than a routes file. The README is direct about the audience and the scope, it is a package for using PHP 8 attributes to register routes in a Laravel app, and the video it links is aimed at people who want to understand how it works rather than just how to use it. The repository is small, with src, tests, a config directory, a composer.json, a phpunit configuration and a CHANGELOG.

Editorial conclusion

laravel-route-attributes suits a Laravel team that wants its route definitions next to the controller methods they belong to, and that is comfortable letting a package scan the source tree to find them. It does not suit a codebase that keeps all routes in one explicit file on purpose, since the package trades that visibility for locality, and it uses PHP attributes; check the selected package version's PHP and Laravel requirements before installing. The current main branch requires PHP 8.3 or newer and Laravel components 11, 12 or 13. Before you adopt it, decide how your controller directories map to route groups, because the directories configuration is where the prefix and middleware come from, and use the patterns and not_patterns options if your tests live beside your controllers.

Frequently asked questions

How do I install spatie/laravel-route-attributes?

Run composer require spatie/laravel-route-attributes, then publish the config with php artisan vendor:publish naming the Spatie\RouteAttributes\RouteAttributesServiceProvider provider and the config tag. The published file has an enabled switch, which must be true for automatic registration to happen.

How do I give a group of controllers a prefix and middleware in laravel-route-attributes?

In the directories configuration, map the controller path to an array with the group settings, so the api controllers directory can be given an api prefix and the api middleware while the web controllers directory gets the web middleware. You can also map a namespace to a path for controllers outside the application root namespace.

Can laravel-route-attributes skip my test files?

Yes, through the patterns and not_patterns options on a directory entry. The example restricts registration to files matching a controller pattern and excludes files matching a test pattern, which matters when you co-locate tests with controllers in the same directory.

How do I register a resource controller with laravel-route-attributes?

Use the Resource attribute, which takes a resource name and accepts only and except to control which routes exist, parameters to change the default route parameters, names to set route names, shallow to limit nesting to routes without a unique child identifier, and apiResource to include only API actions. There is also an ApiResource attribute that extends it with apiResource already set.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. spatie/laravel-route-attributes on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/spatie-laravel-route-attributes.svg)](https://hysenlabs.com/projects/spatie-laravel-route-attributes)

Star history chart for your README

markdown
[![Star history](https://hysenlabs.com/chart/spatie-laravel-route-attributes.svg)](https://hysenlabs.com/projects/spatie-laravel-route-attributes)

Was this useful?