Skip to content

Conversation

AmirHkrg
Copy link

@AmirHkrg AmirHkrg commented Jul 26, 2025

This pull request introduces the foundational support for attribute-based routing in Laravel. It provides a clean, modern, and developer-friendly way to declare routes directly on controller methods.

Discussions

Motivation

As PHP continues to evolve, attributes have become a powerful tool for declarative programming. Many modern frameworks have adopted them for routing, which helps co-locate route definitions with their corresponding logic, improving clarity and developer ergonomics. This PR aims to bring this same modern convenience to Laravel, embracing modern PHP capabilities more deeply.

Crucially, this system is designed to work in conjunction with the traditional file-based routing system, not as a replacement. Developers can use both methods side-by-side in the same application, giving them more flexibility in how they organize their projects.

Proposed Solution

The feature is opt-in and enabled via a new fluent method, withAttributeRouting(), in bootstrap/app.php.

  1. Enabling the Feature
    Developers can easily specify which directories should be scanned for web and api route groups. If called with no arguments, it uses a sensible default.
// in bootstrap/app.php

return Application::configure()
    ->withRouting(
        web: __DIR__.'/../routes/web.php',
    )
    // Example 1: Enable with a sensible default
    // (Scans app/Http/Controllers for the 'web' group)
    ->withAttributeRouting()

    // Example 2: Or, provide explicit configuration
    // ->withAttributeRouting(
    //     web: app_path('Http/Web'),
    //     api: app_path('Http/Api')
    // )
    ->...
  1. Defining Routes
    Controllers are marked for discovery by implementing the AttributeRouteController interface. Routes and groups are then defined with simple attributes:
<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Attributes\{Get, Group, Post};
use Illuminate\Routing\Attributes\AttributeRouteController;

#[Group(prefix: 'users', name: 'users.', middleware: 'auth')]
class UserController extends Controller implements AttributeRouteController
{
    #[Get('/', name: 'index')]
    public function index()
    {
        // Route: GET /users
        // Name: users.index
    }

    #[Post('/', name: 'store')]
    public function store()
    {
        // Route: POST /users
        // Name: users.store
    }
}

Performance Considerations

The performance impact of this feature has been carefully considered. In local development, a negligible overhead of a few milliseconds is introduced by the file scanning process. However, in production, once routes are cached using php artisan route:cache, there is zero performance impact. The only change is a simpler and more enjoyable development workflow.

This is an MVP

This initial pull request implements the Minimum Viable Product (MVP) for this feature. The goal is to first agree on the fundamental architecture and API.

I have already locally developed and tested more advanced features that can be added in subsequent PRs upon approval of this core concept. These future possibilities include:

  • #[Resource] and #[ApiResource] attributes for full RESTful controller support.
  • Advanced #[Group] features like only and except for targeting specific methods.
  • scopeBindings support for both resources and groups.
  • An implicit discovery mode to reduce boilerplate.
  • Enhancements to Artisan commands (route:list and make:controller).

Currently, there are third-party packages available for this purpose, but it’s possible to offer such a feature — which already exists in most modern frameworks — directly within Laravel itself. This would allow for greater compatibility, a more controlled structure, and higher flexibility. For example, editing route commands and similar functionalities that cannot be modified in third-party packages.

It could also be developed in a much more advanced way with newer and more complete ideas, aligned with Laravel’s philosophy.

Of course, I understand that adding more logic and code will make development and maintenance more challenging. However, I am capable of carrying out the full development, and Laravel’s large and active community will also help along the way.

Next Steps & Collaboration

I am fully committed to seeing this feature through to completion. If this initial proposal is accepted by the team and the community, I am ready to collaborate on any necessary changes and submit the subsequent PRs to build out the full feature set.

@AmirHkrg AmirHkrg marked this pull request as draft July 26, 2025 02:06
@AmirHkrg AmirHkrg marked this pull request as ready for review July 26, 2025 02:13
@AmirHkrg AmirHkrg marked this pull request as draft July 26, 2025 02:27
@macropay-solutions
Copy link

The feature is opt-in and enabled via a new fluent method, withAttributeRouting(), in bootstrap/app.php.

Thank you for not making it a default. If this gets merged, symfony lovers will clap their hands like a circus seal.

@antonkomarev
Copy link
Contributor

I have a project, where we have more than one directory of web routes and many API routes directories, some of them are located outside of App namespace. I think this should be more flexible.

@AmirHkrg
Copy link
Author

AmirHkrg commented Sep 1, 2025

I have a project, where we have more than one directory of web routes and many API routes directories, some of them are located outside of App namespace. I think this should be more flexible.

Hi @antonkomarev

You can easily specify a list of folders to scan,
and this feature currently exists in this pull request.

->withAttributeRouting(
    web: [
        base_path('src/Invoicing/UI/Web'),
        base_path('src/Customers/UI/Web'),
    ],
    api: [
        base_path('src/Invoicing/UI/Api'),
        base_path('src/Customers/UI/Api'),
    ]
)

Of course, this is just an MVP version; if its addition is approved, other features will be added as well.

@crynobone crynobone added the needs work Not quite ready for primetime label Sep 11, 2025
@crynobone
Copy link
Member

Test are failling

@AmirHkrg AmirHkrg marked this pull request as ready for review October 3, 2025 14:44
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be an attribute as well, instead of an interface? It seems that things are moving away from using empty interfaces to mark functionality and towards using attributes instead.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hello,
Thanks for the review.

If I understood correctly, you’re referring to the AttributeRouteController interface. In this pull request, I initially made the system explicit by requiring that interface.
If this pull request is accepted, we could also add an implicit mode so that all controllers are scanned without needing to implement the AttributeRouteController interface.

I’d also be happy to hear if you think there’s a better approach that could be implemented.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AmirHkrg I'm suggesting using an attribute instead of an interface since the interface is empty and appears to be used as just a marker.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we want to avoid using an interface for marking altogether, the best approach would be the implicit one — defining routes only with attributes, without any additional requirements.

Copy link
Contributor

@shaedrich shaedrich Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume, you talk past each other. Afaik, Will meant this:

https://www.php.net/manual/en/language.attributes.overview.php

<?php

namespace App\Http\Controllers;

use Illuminate\Routing\Attributes\{Get, Group, Post};
use Illuminate\Routing\Attributes\AttributeRouteController;

#[Group(prefix: 'users', name: 'users.', middleware: 'auth')]
#[AttributeRouteController]
class UserController extends Controller 
{
    #[Get('/', name: 'index')]
    public function index()
    {
        // Route: GET /users
        // Name: users.index
    }

    #[Post('/', name: 'store')]
    public function store()
    {
        // Route: POST /users
        // Name: users.store
    }
}

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @shaedrich

Yes, I understand their point, but I followed the current structure and conventions of Laravel. As you can see, Laravel uses interfaces for marking, such as the ShouldQueue and ShouldBeUnique interfaces in queues.
It is possible to use attributes for marking as well, but it would work a bit differently compared to other features.

Copy link
Contributor

@shaedrich shaedrich Oct 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey Amir, no offense, but this is short-sighted. Of course, Laravel has that structure. This is for historical reasons. When Laravel was created, there were no attributes. And if we would rectify everything with "we've always done it that way" and limit ourselves to only what had already been available at that time, there would be no progress.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Certainly, if I look at it from your perspective, it’s completely valid, and it could be really good to be a pioneer in using new approaches.
I’ll replace the interface with attributes, though having both together might not be a bad idea either.
Of course, it’s also important that the Laravel team agrees with us, since this pull request itself might have a low chance of being merged.

@taylorotwell
Copy link
Member

Thanks for your pull request to Laravel!

Unfortunately, I'm going to delay merging this code for now. To preserve our ability to adequately maintain the framework, we need to be very careful regarding the amount of code we include.

If applicable, please consider releasing your code as a package so that the community can still take advantage of your contributions!

@taylorotwell
Copy link
Member

There are great attribute routing packages in the community 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
needs work Not quite ready for primetime
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants