PHPackages                             ejunker/laravel-route-attributes - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. ejunker/laravel-route-attributes

ActiveLibrary[API Development](/categories/api)

ejunker/laravel-route-attributes
================================

Auto register routes using PHP attributes

1.18.8(1mo ago)0271MITPHPPHP ^8.0

Since Jun 29Pushed 1mo agoCompare

[ Source](https://github.com/ejunker/laravel-route-attributes)[ Packagist](https://packagist.org/packages/ejunker/laravel-route-attributes)[ Docs](https://github.com/ejunker/laravel-route-attributes)[ GitHub Sponsors](https://github.com/sponsors/spatie)[ Fund](https://spatie.be/open-source/support-us)[ RSS](/packages/ejunker-laravel-route-attributes/feed)WikiDiscussions main Synced 1mo ago

READMEChangelogDependencies (8)Versions (9)Used By (0)

Use PHP 8 attributes to register routes in a Laravel app
========================================================

[](#use-php-8-attributes-to-register-routes-in-a-laravel-app)

[![Latest Version on Packagist](https://camo.githubusercontent.com/a0c99854bd22e8df70b2af98612f082bd18789e7cfef06ac8bc323789587b79a/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f7370617469652f6c61726176656c2d726f7574652d617474726962757465732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/spatie/laravel-route-attributes)[![Tests](https://github.com/spatie/laravel-route-attributes/workflows/Tests/badge.svg)](https://github.com/spatie/laravel-route-attributes/workflows/Tests/badge.svg)[![Type Coverage](https://camo.githubusercontent.com/2948d9bc332bd7f0753afcba62bb81f049f5308352d2ac01ec3192b2ddee038b/68747470733a2f2f73686570686572642e6465762f6769746875622f7370617469652f6c61726176656c2d726f7574652d617474726962757465732f636f7665726167652e737667)](https://shepherd.dev/github/spatie/laravel-route-attributes)[![Total Downloads](https://camo.githubusercontent.com/191af57004c89bd2f0adbeae76ae0995e63b48608ea658f8ac1f5ddcec4f2699/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f7370617469652f6c61726176656c2d726f7574652d617474726962757465732e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/spatie/laravel-route-attributes)

This package provides annotations to automatically register routes. Here's a quick example:

```
use Spatie\RouteAttributes\Attributes\Get;

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

This attribute will automatically register this route:

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

Are you a visual learner?
-------------------------

[](#are-you-a-visual-learner)

[In this video](https://spatie.be/videos/front-line-php/adding-meta-data-using-attributes) you'll get an introduction to PHP 8 attributes and how this laravel-routes-attributes works under the hood.

Support us
----------

[](#support-us)

[![](https://camo.githubusercontent.com/f1310a5a220f080a1646d5f1b2e5bd61ab81e8d9497d06c2a460a7d2f49bd107/68747470733a2f2f6769746875622d6164732e73332e65752d63656e7472616c2d312e616d617a6f6e6177732e636f6d2f6c61726176656c2d726f7574652d617474726962757465732e6a70673f743d32)](https://spatie.be/github-ad-click/laravel-route-attributes)

We invest a lot of resources into creating [best in class open source packages](https://spatie.be/open-source). You can support us by [buying one of our paid products](https://spatie.be/open-source/support-us).

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on [our contact page](https://spatie.be/about-us). We publish all received postcards on [our virtual postcard wall](https://spatie.be/open-source/postcards).

Installation
------------

[](#installation)

You can install the package via composer:

```
composer require spatie/laravel-route-attributes
```

You can publish the config file with:

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

This is the contents of the published config file:

```
return [
    /*
     *  Automatic registration of routes will only happen if this setting is `true`
     */
    'enabled' => true,

    /*
     * Controllers in these directories that have routing attributes
     * will automatically be registered.
     *
     * Optionally, you can specify group configuration by using key/values
     */
    'directories' => [
        app_path('Http/Controllers'),

        app_path('Http/Controllers/Web') => [
            'middleware' => ['web']
        ],

        app_path('Http/Controllers/Api') => [
            'prefix' => 'api',
            'middleware' => 'api'
        ],
    ],
];
```

For controllers outside the applications root namespace directories can also be added using a `namespace => path` pattern in the directories array. In the following example controllers from `Modules\Admin\Http\Controllers` will be included.

```
'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'),
],
```

Usage
-----

[](#usage)

The package provides several annotations that should be put on controller classes and methods. These annotations will be used to automatically register routes

### Adding a GET route

[](#adding-a-get-route)

```
use Spatie\RouteAttributes\Attributes\Get;

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

    }
}
```

This attribute will automatically register this route:

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

### Using other HTTP verbs

[](#using-other-http-verbs)

We have left no HTTP verb behind. You can use these attributes on controller methods.

```
#[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')]
```

### Resource controllers

[](#resource-controllers)

To register a [resource controller](https://laravel.com/docs/controllers#resource-controllers), use the `Resource` attribute as shown in the example below.

You can use `only` or `except` parameters to manage your resource routes availability.

You can use `parameters` parameter to modify the default parameters set by the resource attribute.

You can use the `names` parameter to set the route names for the resource controller actions. Pass a string value to set a base route name for each controller action or pass an array value to define the route name for each controller action.

You can use `shallow` parameter to make a nested resource to apply nesting only to routes without a unique child identifier (`index`, `create`, `store`).

You can use `apiResource` boolean parameter to only include actions used in APIs. Alternatively, you can use the `ApiResource` attribute, which extends the `Resource` attribute class, but the parameter `apiResource` is already set to `true`.

Using `Resource` attribute with `Domain`, `Prefix` and `Middleware` attributes works as well.

```
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
{
    public function index(Photo $photo)
    {
    }

    public function store(Request $request, Photo $photo)
    {
    }

    public function show(Comment $comment)
    {
    }

    public function update(Request $request, Comment $comment)
    {
    }
}
```

The attribute in the example above will automatically register following routes:

```
Route::get('api/v1/photos/{photo}/comments', [PhotoCommentController::class, 'index'])->name('api.v1.photoComments.index');
Route::post('api/v1/photos/{photo}/comments', [PhotoCommentController::class, 'store'])->name('api.v1.photoComments.store');
Route::get('api/v1/comments/{comment}', [PhotoCommentController::class, 'show'])->name('api.v1.photoComments.show');
Route::match(['put', 'patch'], 'api/v1/comments/{comment}', [PhotoCommentController::class, 'update'])->name('api.v1.photoComments.update');
```

### Using multiple verbs

[](#using-multiple-verbs)

To register a route for all verbs, you can use the `Any` attribute:

```
#[Spatie\RouteAttributes\Attributes\Any('my-uri')]
```

To register a route for a few verbs at once, you can use the `Route` attribute directly:

```
#[Spatie\RouteAttributes\Attributes\Route(['put', 'patch'], 'my-uri')]
```

### Specify a route name

[](#specify-a-route-name)

All HTTP verb attributes accept a parameter named `name` that accepts a route name.

```
use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route', name: "my-route-name")]
    public function myMethod()
    {
    }
}
```

This attribute will automatically register this route:

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

### Adding middleware

[](#adding-middleware)

All HTTP verb attributes accept a parameter named `middleware` that accepts a middleware class or an array of middleware classes.

```
use Spatie\RouteAttributes\Attributes\Get;

class MyController
{
    #[Get('my-route', middleware: MyMiddleware::class)]
    public function myMethod()
    {

    }
}
```

This annotation will automatically register this route:

```
Route::get('my-route', [MyController::class, 'myMethod'])->middleware(MyMiddleware::class);
```

To apply middleware on all methods of a class you can use the `Middleware` attribute. You can mix this with applying attribute on a method.

```
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Middleware;

#[Middleware(MyMiddleware::class)]
class MyController
{
    #[Get('my-route')]
    public function firstMethod()
    {
    }

    #[Get('my-other-route', middleware: MyOtherMiddleware::class)]
    public function secondMethod()
    {
    }
}
```

These annotations will automatically register these routes:

```
Route::get('my-route', [MyController::class, 'firstMethod'])->middleware(MyMiddleware::class);
Route::get('my-other-route', [MyController::class, 'secondMethod'])->middleware([MyMiddleware::class, MyOtherMiddleware::class]);
```

### Specifying a prefix

[](#specifying-a-prefix)

You can use the `Prefix` annotation on a class to prefix the routes of all methods of that class.

```
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Prefix;

#[Prefix('my-prefix')]
class MyController
{
    #[Get('my-get-route')]
    public function myGetMethod()
    {
    }

    #[Post('my-post-route')]
    public function myPostMethod()
    {
    }
}
```

These annotations will automatically register these routes:

```
Route::get('my-prefix/my-get-route', [MyController::class, 'myGetMethod']);
Route::post('my-prefix/my-post-route', [MyController::class, 'myPostMethod']);
```

### Specifying a domain

[](#specifying-a-domain)

You can use the `Domain` annotation on a class to prefix the routes of all methods of that class.

```
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Domain;

#[Domain('my-subdomain.localhost')]
class MyController
{
    #[Get('my-get-route')]
    public function myGetMethod()
    {
    }

    #[Post('my-post-route')]
    public function myPostMethod()
    {
    }
}
```

These annotations will automatically register these routes:

```
Route::get('my-get-route', [MyController::class, 'myGetMethod'])->domain('my-subdomain.localhost');
Route::post('my-post-route', [MyController::class, 'myPostMethod'])->domain('my-subdomain.localhost');
```

### Specifying a domain from a config key

[](#specifying-a-domain-from-a-config-key)

There maybe a need to define a domain from a configuration file, for example where your subdomain will be different on your development environment to your production environment.

```
// config/domains.php
return [
    'main' => env('SITE_URL', 'example.com'),
    'subdomain' => env('SUBDOMAIN_URL', 'subdomain.example.com')
];
```

```
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\DomainFromConfig;

#[DomainFromConfig('domains.main')]
class MyController
{
    #[Get('my-get-route')]
    public function myGetMethod()
    {
    }
}
```

When this is parsed, it will get the value of `domains.main` from the config file and register the route as follows;

```
Route::get('my-get-route', [MyController::class, 'myGetMethod'])->domain('example.com');
```

### Scoping bindings

[](#scoping-bindings)

When implicitly binding multiple Eloquent models in a single route definition, you may wish to scope the second Eloquent model such that it must be a child of the previous Eloquent model.
By adding the `ScopeBindings` annotation, you can enable this behaviour:

```
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\ScopeBindings;

class MyController
{
    #[Get('users/{user}/posts/{post}')]
    #[ScopeBindings]
    public function getUserPost(User $user, Post $post)
    {
        return $post;
    }
}
```

This is akin to using the `->scopeBindings()` method on the route registrar manually:

```
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
    return $post;
})->scopeBindings();
```

You can also use the annotation on controllers to enable implicitly scoped bindings for all its methods.

### Specifying where

[](#specifying-where)

You can use the `Where` annotation on a class or method to constrain the format of your route parameters.

```
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Where;
use Spatie\RouteAttributes\Attributes\WhereAlphaNumeric;

#[Where('my-where', '[0-9]+')]
class MyController
{
    #[Get('my-get-route/{my-where}')]
    public function myGetMethod()
    {
    }

    #[Post('my-post-route/{my-where}/{my-alpha-numeric}')]
    #[WhereAlphaNumeric('my-alpha-numeric')]
    public function myPostMethod()
    {
    }
}
```

These annotations will automatically register these routes:

```
Route::get('my-get-route/{my-where}', [MyController::class, 'myGetMethod'])->where(['my-where' => '[0-9]+']);
Route::post('my-post-route/{my-where}/{my-alpha-numeric}', [MyController::class, 'myPostMethod'])->where(['my-where' => '[0-9]+', 'my-alpha-numeric' => '[a-zA-Z0-9]+']);
```

For convenience, some commonly used regular expression patterns have helper attributes that allow you to quickly add pattern constraints to your routes.

```
#[WhereAlpha('alpha')]
#[WhereAlphaNumeric('alpha-numeric')]
#[WhereIn('in', ['value1', 'value2'])]
#[WhereNumber('number')]
#[WhereUlid('ulid')]
#[WhereUuid('uuid')]
```

### Specifying a group

[](#specifying-a-group)

You can use the `Group` annotation on a class to create multiple groups with different domains and prefixes for the routes of all methods of that class.

```
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;
use Spatie\RouteAttributes\Attributes\Domain;

#[Group(domain: 'my-subdomain.localhost', prefix: 'my-prefix')]
#[Group(domain: 'my-second-subdomain.localhost', prefix: 'my-second-prefix')]
class MyController
{
    #[Get('my-get-route')]
    public function myGetMethod()
    {
    }

    #[Post('my-post-route')]
    public function myPostMethod()
    {
    }
}
```

These annotations will automatically register these routes:

```
Route::get('my-get-route', [MyController::class, 'myGetMethod'])->prefix('my-prefix')->domain('my-subdomain.localhost');
Route::post('my-post-route', [MyController::class, 'myPostMethod'])->prefix('my-prefix')->domain('my-subdomain.localhost');
Route::get('my-get-route', [MyController::class, 'myGetMethod'])->prefix('my-second-prefix')->domain('my-second-subdomain.localhost');
Route::post('my-post-route', [MyController::class, 'myPostMethod'])->prefix('my-second-prefix')->domain('my-second-subdomain.localhost');
```

### Specifying defaults

[](#specifying-defaults)

You can use the `Defaults` annotation on a class or method to define the default values of your optional route parameters.

```
use Spatie\RouteAttributes\Attributes\Defaults;
use Spatie\RouteAttributes\Attributes\Get;
use Spatie\RouteAttributes\Attributes\Post;

#[Defaults('param', 'controller-default')]
class MyController extends Controller
{
    #[Get('my-get-route/{param?}')]
    public function myGetMethod($param)
    {
    }

    #[Post('my-post-route/{param?}/{param2?}')]
    #[Defaults('param2', 'method-default')]
    public function myPostMethod($param, $param2)
    {
    }

    #[Get('my-default-route/{param?}/{param2?}/{param3?}')]
    #[Defaults('param2', 'method-default-first')]
    #[Defaults('param3', 'method-default-second')]
    public function myDefaultMethod($param, $param2, $param3)
    {
    }

    #[Get('my-override-route/{param?}')]
    #[Defaults('param', 'method-default')]
    public function myOverrideMethod($param)
    {
    }
}
```

These annotations will automatically register these routes:

```
Route::get('my-get-route/{param?}', [MyController::class, 'myGetMethod'])->setDefaults(['param', 'controller-default']);
Route::post('my-post-route/{param?}/{param2?}', [MyController::class, 'myPostMethod'])->setDefaults(['param', 'controller-default', 'param2' => 'method-default']);
Route::get('my-default-route/{param?}/{param2?}/{param3?}', [MyController::class, 'myDefaultMethod'])->setDefaults(['param', 'controller-default', 'param2' => 'method-default-first', 'param3' => 'method-default-second']);
Route::get('my-override-route/{param?}', [MyController::class, 'myOverrideMethod'])->setDefaults(['param', 'method-default']);
```

Testing
-------

[](#testing)

```
composer test
```

Changelog
---------

[](#changelog)

Please see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.

Contributing
------------

[](#contributing)

Please see [CONTRIBUTING](https://github.com/spatie/.github/blob/main/CONTRIBUTING.md) for details.

Security Vulnerabilities
------------------------

[](#security-vulnerabilities)

Please review [our security policy](../../security/policy) on how to report security vulnerabilities.

Credits
-------

[](#credits)

- [Freek Van der Herten](https://github.com/freekmurze)
- [All Contributors](../../contributors)

License
-------

[](#license)

The MIT License (MIT). Please see [License File](LICENSE.md) for more information.

###  Health Score

46

—

FairBetter than 93% of packages

Maintenance91

Actively maintained with recent releases

Popularity14

Limited adoption so far

Community19

Small or concentrated contributor base

Maturity54

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 53.1% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~201 days

Recently: every ~251 days

Total

6

Last Release

43d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/94f5faf8e6d32349cbaf2ebf6cd84b0eb2565f44f3429950f0b06d626ef315bd?d=identicon)[ejunker](/maintainers/ejunker)

---

Top Contributors

[![freekmurze](https://avatars.githubusercontent.com/u/483853?v=4)](https://github.com/freekmurze "freekmurze (164 commits)")[![osbre](https://avatars.githubusercontent.com/u/23292709?v=4)](https://github.com/osbre "osbre (15 commits)")[![patinthehat](https://avatars.githubusercontent.com/u/5508707?v=4)](https://github.com/patinthehat "patinthehat (12 commits)")[![Tofandel](https://avatars.githubusercontent.com/u/6115458?v=4)](https://github.com/Tofandel "Tofandel (10 commits)")[![robertdrakedennis](https://avatars.githubusercontent.com/u/31261583?v=4)](https://github.com/robertdrakedennis "robertdrakedennis (10 commits)")[![Radiergummi](https://avatars.githubusercontent.com/u/6115429?v=4)](https://github.com/Radiergummi "Radiergummi (9 commits)")[![ejunker](https://avatars.githubusercontent.com/u/4758?v=4)](https://github.com/ejunker "ejunker (9 commits)")[![midnite81](https://avatars.githubusercontent.com/u/254850?v=4)](https://github.com/midnite81 "midnite81 (8 commits)")[![leonardocaldas](https://avatars.githubusercontent.com/u/17347635?v=4)](https://github.com/leonardocaldas "leonardocaldas (8 commits)")[![sirmathays](https://avatars.githubusercontent.com/u/37704147?v=4)](https://github.com/sirmathays "sirmathays (7 commits)")[![danielsetreus](https://avatars.githubusercontent.com/u/2234052?v=4)](https://github.com/danielsetreus "danielsetreus (7 commits)")[![serkanerip](https://avatars.githubusercontent.com/u/8780913?v=4)](https://github.com/serkanerip "serkanerip (6 commits)")[![jaulz](https://avatars.githubusercontent.com/u/5358638?v=4)](https://github.com/jaulz "jaulz (5 commits)")[![Nielsvanpach](https://avatars.githubusercontent.com/u/10651054?v=4)](https://github.com/Nielsvanpach "Nielsvanpach (4 commits)")[![asharifineyestani](https://avatars.githubusercontent.com/u/12456855?v=4)](https://github.com/asharifineyestani "asharifineyestani (4 commits)")[![dependabot[bot]](https://avatars.githubusercontent.com/in/29110?v=4)](https://github.com/dependabot[bot] "dependabot[bot] (4 commits)")[![brendt](https://avatars.githubusercontent.com/u/6905297?v=4)](https://github.com/brendt "brendt (4 commits)")[![AdrianMrn](https://avatars.githubusercontent.com/u/12762044?v=4)](https://github.com/AdrianMrn "AdrianMrn (4 commits)")[![github-actions[bot]](https://avatars.githubusercontent.com/in/15368?v=4)](https://github.com/github-actions[bot] "github-actions[bot] (3 commits)")[![samyrataylor](https://avatars.githubusercontent.com/u/15961687?v=4)](https://github.com/samyrataylor "samyrataylor (3 commits)")

---

Tags

spatielaravel-route-attributes

###  Code Quality

TestsPHPUnit

### Embed Badge

![Health badge](/badges/ejunker-laravel-route-attributes/health.svg)

```
[![Health](https://phpackages.com/badges/ejunker-laravel-route-attributes/health.svg)](https://phpackages.com/packages/ejunker-laravel-route-attributes)
```

###  Alternatives

[spatie/laravel-route-attributes

Auto register routes using PHP attributes

879963.2k18](/packages/spatie-laravel-route-attributes)[spatie/laravel-fractal

An easy to use Fractal integration for Laravel applications

1.9k15.1M99](/packages/spatie-laravel-fractal)[spatie/laravel-query-builder

Easily build Eloquent queries from API requests

4.4k26.9M220](/packages/spatie-laravel-query-builder)[spatie/laravel-honeypot

Preventing spam submitted through forms

1.6k6.0M60](/packages/spatie-laravel-honeypot)[spatie/fractalistic

A developer friendly wrapper around Fractal

38715.3M8](/packages/spatie-fractalistic)[spatie/laravel-route-discovery

Auto register routes using PHP attributes

23645.0k2](/packages/spatie-laravel-route-discovery)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
