pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/cakephp/cakephp/pull/19293

sets/global-52276e82f63bb403.css" /> 6.x Adding Attribute routing by josbeir · Pull Request #19293 · cakephp/cakephp · GitHub
Skip to content

6.x Adding Attribute routing#19293

Open
josbeir wants to merge 12 commits into6.xfrom
6.x-attribute-routing
Open

6.x Adding Attribute routing#19293
josbeir wants to merge 12 commits into6.xfrom
6.x-attribute-routing

Conversation

@josbeir
Copy link
Contributor

@josbeir josbeir commented Feb 21, 2026

Refs #19228

Summary

Implements the attribute-routing RFC from #19228 by introducing route attributes, wiring attribute discovery/connection into routing, and aligning controller action argument binding for named/positional route params.

This also extends AttributeResolver metadata so routing can make controller-instantiability and action-eligibility decisions without reflection in the connector layer.

What changed

  • Added routing attributes for controller/action mapping:
    • Route, HTTP method shortcuts (Get, Post, Put, Patch, Delete, Options, Head)
    • class-level attributes: Scope, Prefix, RouteClass, Middleware, Extensions, Resource
  • Added AttributeRouteConnector to:
    • aggregate class/method attribute metadata
    • connect method routes and resource routes
    • derive _argsByName metadata from pass/placeholders
    • skip non-instantiable controller targets via resolver metadata
    • ignore non-public method targets using resolver-provided method visibility metadata
  • Added RouteBuilder::attributes() integration to connect resolver-discovered routes.
  • Updated ControllerFactory to support _argsByName mapping while preserving existing positional/named pass behavior.
  • Kept compatibility behavior for non-attribute routing (no strict route-placeholder vs action-signature exception by default).

AttributeResolver updates (and why)

  • Extended AttributeTarget metadata with:
    • declaringClassType (class / interface / trait / enum)
    • isDeclaringClassAbstract
    • methodVisibility (public / protected / private, for method-related targets)
    • computed helpers: isInstantiableDeclaringType() and isPublicMethodTarget()
  • Added MethodVisibility enum.
  • Updated Parser to populate class-type/abstractness metadata once per class and method visibility once per method, then propagate into method/parameter targets.

Why this was needed:

  • Routing must ignore abstract or non-class controller declarations.
  • Routing should only connect public action methods.
  • Centralizing this in resolver metadata removes connector reflection checks, keeps connector logic simpler, and makes metadata reusable for other consumers.

Forward-looking note

  • The new methodVisibility metadata also opens a path for future “auto-scoped routes” mode, where all public controller methods inside a scope could be connected automatically without relying on fallback routing.

Usage examples (after setting up AttributeResolver)

// config/routes.php

$routes->connectAttributes();

$routes->scope('/', function (\Cake\Routing\RouteBuilder $routes): void {
   // Manual routes...
});
// src/Controller/ArticlesController.php
use Cake\Routing\Attribute\Get;
use Cake\Routing\Attribute\Scope;

#[Scope('/articles', namePrefix: 'articles:')]
class ArticlesController extends AppController
{
    #[Get('/', name: 'index')]
    public function index(): void
    {
    }

    #[Get('/{id}', name: 'view')]
    public function view(string $id): void
    {
    }
}
use Cake\Routing\Attribute\Resource;

#[Resource('articles', only: ['index', 'view'])]
class ArticlesController extends AppController
{
}

@josbeir josbeir added this to the 6.0 milestone Feb 21, 2026
@josbeir josbeir self-assigned this Feb 21, 2026
Copilot AI review requested due to automatic review settings February 21, 2026 12:38
@josbeir josbeir changed the title 6.x attribute routing 6.x Adding Attribute routing Feb 21, 2026

This comment was marked as outdated.

@josbeir josbeir force-pushed the 6.x-attribute-routing branch from 6f471ae to a8834d2 Compare February 21, 2026 16:12
@josbeir josbeir force-pushed the 6.x-attribute-routing branch from a8834d2 to 77ca3ef Compare February 21, 2026 16:23
@LordSimal
Copy link
Contributor

  • Should HTTP method-style attributes remain Get/Post/Put/... (current), or should naming move toward an uppercase style?

I have forgotten, that it would break our code style rules - therefore I am fine having the PascalCase

Copy link
Contributor

@LordSimal LordSimal left a comment

Choose a reason for hiding this comment

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

Love so see this finalized and working 🥳

…rs for extra guarding and attribute based automatic route connecting

This comment was marked as outdated.

This comment was marked as outdated.

@ADmad
Copy link
Member

ADmad commented Feb 23, 2026

@josbeir In your usage example the $routes->attributes(); in inside a scope. It should be moved out to avoid someone incorrectly thinking that the call needs to be done per scope.

@josbeir
Copy link
Contributor Author

josbeir commented Feb 23, 2026

@josbeir In your usage example the $routes->attributes(); in inside a scope. It should be moved out to avoid someone incorrectly thinking that the call needs to be done per scope.

Updated, but it actually works like you'd expect. If you put it in a scope then all discovered attributes from a resolver config (which is the only optional argument of attributes() would follow that scope.

So yes for root attributes it can be added inside the '/' scope or outside, they will all start at root level. The example illustrated that it also work likes that (handy feature actually 😎)

@ADmad
Copy link
Member

ADmad commented Feb 23, 2026

Updated, but it actually works like you'd expect. If you put it in a scope then all discovered attributes from a resolver config (which is the only optional argument of attributes() would follow that scope.

Oh, so if I do $routes->scope('/admin', function () { $routes->attributes() }); only, then any attributes I might might set like #[Scope('/articles', namePrefix: 'articles:')] will be ignored?

@josbeir
Copy link
Contributor Author

josbeir commented Feb 23, 2026

Oh, so if I do $routes->scope('/admin', function () { $routes->attributes() }); only, then any attributes I might might set like #[Scope('/articles', namePrefix: 'articles:')] will be ignored?

No, they just will all be connected to the /admin scope.
So if you set an a Scope attribute for articles then it will be connected to /admin scope. = /admin/articles/....

// routes.php
$routes->scope('/admin', function(RouteBuilder $routes) {
   $routes->attributes();
});

// A controller
#[Scope('/articles')]
class ArticlesController extends AppController {
   
   #[Get('/list')]
   public function index() {
   }
}

will result in /admin/articles/list

Basically the resolver holds the list of discovered route attributes. Then these routes are connected to the scope where attributes() was called in.

So if a resolver configuration is there that targets a specific path/pattern then that config could be used to connect a specific subset of routes to a scope. Not something we should explicitly promote but it is a use case that could be handy.

@LordSimal
Copy link
Contributor

@markstory @dereuromark thoughts?

@josbeir josbeir force-pushed the 6.x-attribute-routing branch from 7df7506 to a839d54 Compare March 14, 2026 08:14
@josbeir josbeir force-pushed the 6.x-attribute-routing branch from cbe1660 to ae4c935 Compare March 17, 2026 14:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy