foundation/documentation/attribute-routing.md
2025-06-13 18:29:55 +02:00

6.3 KiB

Attribute-Based Routing

The Foundation framework provides a modern attribute-based routing system that automatically discovers and registers routes when controllers are registered in the dependency injection container.

Overview

Instead of manually registering routes in service providers, controllers can use PHP 8+ attributes to define their routes directly on controller methods. Routes are automatically processed when controllers are registered in the container.

Key Components

ControllerInterface

Controllers must implement Foundation\Core\Infrastructure\ControllerInterface to have their route attributes processed:

use Foundation\Core\Infrastructure\ControllerInterface;

class MyController implements ControllerInterface
{
    // Route attributes will be processed for this controller
}

Route Attributes

Basic Route Attributes

  • #[Get('/path')] - GET requests
  • #[Post('/path')] - POST requests
  • #[Put('/path')] - PUT requests
  • #[Delete('/path')] - DELETE requests
  • #[Route('/path', ['GET', 'POST'])] - Custom HTTP methods

Group Attribute

Use #[Group('/prefix')] on controller classes to add a common prefix to all routes:

#[Group('/api/users')]
class UserController implements ControllerInterface
{
    #[Get('')]          // Route: GET /api/users
    #[Get('/{id}')]     // Route: GET /api/users/{id}
    #[Post('')]         // Route: POST /api/users
}

Route Options

All route attributes support additional options:

#[Get('/path', name: 'route.name', middleware: [AuthMiddleware::class])]
#[Group('/api', middleware: [RateLimitMiddleware::class])]

Examples

Basic Controller

<?php

namespace App\Infrastructure\Web;

use Foundation\Core\Infrastructure\ControllerInterface;
use Foundation\Core\Routing\Attributes\Get;
use Foundation\Core\Routing\Attributes\Post;

class ProductController implements ControllerInterface
{
    #[Get('/products')]
    public function index(): ResponseInterface
    {
        // Handle GET /products
    }

    #[Get('/products/{id}')]
    public function show(string $id): ResponseInterface
    {
        // Handle GET /products/{id}
    }

    #[Post('/products')]
    public function create(): ResponseInterface
    {
        // Handle POST /products
    }
}

API Controller with Groups

<?php

namespace App\Infrastructure\Api;

use Foundation\Core\Infrastructure\ControllerInterface;
use Foundation\Core\Routing\Attributes\Get;
use Foundation\Core\Routing\Attributes\Post;
use Foundation\Core\Routing\Attributes\Group;

#[Group('/api/v1/products')]
class ProductApiController implements ControllerInterface
{
    #[Get('')]
    public function index(): ResponseInterface
    {
        // Handle GET /api/v1/products
    }

    #[Get('/{id}')]
    public function show(string $id): ResponseInterface
    {
        // Handle GET /api/v1/products/{id}
    }

    #[Post('')]
    public function create(): ResponseInterface
    {
        // Handle POST /api/v1/products
    }
}

Routes with Middleware

<?php

namespace App\Infrastructure\Api;

use Foundation\Core\Infrastructure\ControllerInterface;
use Foundation\Core\Routing\Attributes\Get;
use Foundation\Core\Routing\Attributes\Post;
use Foundation\Core\Routing\Attributes\Group;
use App\Middleware\AuthMiddleware;
use App\Middleware\AdminMiddleware;

#[Group('/admin', middleware: [AuthMiddleware::class, AdminMiddleware::class])]
class AdminController implements ControllerInterface
{
    #[Get('/dashboard')]
    public function dashboard(): ResponseInterface
    {
        // Handle GET /admin/dashboard
        // AuthMiddleware and AdminMiddleware applied
    }

    #[Post('/users', middleware: [ValidateUserMiddleware::class])]
    public function createUser(): ResponseInterface
    {
        // Handle POST /admin/users
        // AuthMiddleware, AdminMiddleware, and ValidateUserMiddleware applied
    }
}

How It Works

Automatic Processing

  1. Container Registration: When a controller is registered in the DI container using $container->register(MyController::class), the system automatically checks if it implements ControllerInterface

  2. Attribute Discovery: If the controller implements the interface, the AttributeRouteProcessor scans the controller for route attributes

  3. Route Registration: Found route attributes are immediately registered with the Slim application

  4. No Bootstrap Phase: Routes are registered during container setup, not during application bootstrap

Processing Flow

Controller Registration
        ↓
ControllerInterface Check
        ↓
Route Attribute Discovery
        ↓
Slim Route Registration

Architecture Benefits

Container-Driven Processing

  • Only processes controllers that are explicitly registered in the container
  • No filesystem scanning or discovery phase
  • Routes registered immediately when controllers are registered

Type Safety

  • Explicit interface contract (ControllerInterface)
  • Clear intent - controllers must opt-in to route processing
  • IDE support and autocompletion

Extensible Design

  • Built on generic AttributeProcessorInterface
  • Framework ready for other attribute types (validation, caching, etc.)
  • Modular and maintainable architecture

Best Practices

Controller Organization

  • Keep controllers focused and single-purpose
  • Use groups for logical route prefixes
  • Implement ControllerInterface only on actual controllers

Route Design

  • Use descriptive route paths
  • Group related routes using #[Group]
  • Apply middleware at the appropriate level (group vs individual routes)

Module Structure

src/Modules/{ModuleName}/Infrastructure/
├── Api/           # API controllers with /api groups
├── Web/           # Web controllers for HTML responses
└── Console/       # Console commands (no route attributes)

Troubleshooting

Routes Not Registered

  1. Ensure controller implements ControllerInterface
  2. Verify controller is registered in a service provider
  3. Check attribute syntax and imports

Route Conflicts

  • Use unique route paths
  • Check for overlapping group prefixes
  • Verify HTTP method combinations

Middleware Issues

  • Ensure middleware classes exist and are registered
  • Check middleware order (group middleware runs before route middleware)
  • Verify middleware implements correct interface