vagkalosynakis/league-router-attributes

Attribute-based routing for league/route (PHP 8+)

Maintainers

Package info

github.com/vagkalosynakis/league-router-attributes

pkg:composer/vagkalosynakis/league-router-attributes

Statistics

Installs: 1

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

v0.1.0 2026-02-10 15:23 UTC

This package is auto-updated.

Last update: 2026-04-10 16:01:48 UTC


README

PHP Version League Route License

Attribute-based routing for league/route. Define routes using PHP 8 attributes directly on controller methods, eliminating manual route registration boilerplate.

Requirements

  • PHP ^8.0
  • league/route ^5.1
  • psr/container ^1.0|^2.0

Installation

composer require vagkalosynakis/league-router-attributes

Usage

Basic Routing

Use the #[Route] attribute to declare routes on controller methods:

<?php

namespace App\Controllers;

use VagKalosynakis\LeagueRouteAttributes\Attributes\Route;

class UserController
{
    #[Route(['GET'], '/users')]
    public function index(): Response
    {
        // List users
    }

    #[Route(['GET'], '/users/{id:\d+}')]
    public function show(int $id): Response
    {
        // Show user
    }

    #[Route(['POST'], '/users')]
    public function store(Request $request): Response
    {
        // Create user
    }

    #[Route(['PUT', 'PATCH'], '/users/{id:\d+}')]
    public function update(int $id, Request $request): Response
    {
        // Update user - handles both PUT and PATCH
    }

    #[Route(['DELETE'], '/users/{id:\d+}')]
    public function destroy(int $id): Response
    {
        // Delete user
    }
}

Route Prefixes and Names

#[Route(['GET'], '/admin/users', prefix: '/api/v1', name: 'admin.users')]
public function adminUsers(): Response
{
    // Accessible at: /api/v1/admin/users
    // Named route: admin.users
}

Middleware

Apply middleware at class level (inherited by all methods) or method level:

<?php

namespace App\Controllers;

use VagKalosynakis\LeagueRouteAttributes\Attributes\Route;
use VagKalosynakis\LeagueRouteAttributes\Attributes\Middleware;

#[Middleware([AuthMiddleware::class])]
class AdminController
{
    #[Route(['GET'], '/dashboard')]
    public function dashboard(): Response
    {
        // Inherits AuthMiddleware from class
    }

    #[Route(['GET'], '/users')]
    #[Middleware([AdminOnlyMiddleware::class])]
    public function users(): Response
    {
        // Has both AuthMiddleware and AdminOnlyMiddleware
    }
}

Excluding Middleware

Use #[WithoutMiddleware] to exclude inherited middleware from specific routes:

<?php

namespace App\Controllers;

use VagKalosynakis\LeagueRouteAttributes\Attributes\Route;
use VagKalosynakis\LeagueRouteAttributes\Attributes\Middleware;
use VagKalosynakis\LeagueRouteAttributes\Attributes\WithoutMiddleware;

#[Middleware([AuthMiddleware::class])]
class ApiController
{
    #[Route(['GET'], '/profile')]
    public function profile(): Response
    {
        // Protected: requires AuthMiddleware
    }

    #[Route(['GET'], '/health')]
    #[WithoutMiddleware([AuthMiddleware::class])]
    public function health(): Response
    {
        // Public: AuthMiddleware excluded
    }
}

Route Discovery

Register routes automatically by scanning a directory:

<?php

use League\Route\Router;
use VagKalosynakis\LeagueRouteAttributes\Discovery\RouteDiscovery;

$router = new Router();
$container = /* your PSR-11 container */;

$discovery = new RouteDiscovery($router, $container);
$discovery->discoverRoutes(__DIR__ . '/src/Controllers');

// Routes are now registered with league/route
$response = $router->dispatch($request);

Manual Registration

Or register specific controllers:

<?php

$discovery = new RouteDiscovery($router, $container);
$discovery->registerRoutesForController(UserController::class);
$discovery->registerRoutesForController(AdminController::class);

How It Works

The package uses PHP's Reflection API to scan classes for route attributes, then registers them with league/route using its standard API. All league/route features (middleware, route groups, strategies, etc.) work as expected.

Supported HTTP Methods

GET, POST, PUT, PATCH, DELETE, HEAD, OPTIONS

Multiple HTTP Methods

A single route can handle multiple HTTP methods:

#[Route(['GET', 'HEAD'], '/users')]
public function index(): Response
{
    // Responds to both GET and HEAD requests
}

Route Parameters

Use league/route's parameter syntax with optional regex constraints:

#[Route(['GET'], '/users/{id:\d+}')]              // Numeric ID
#[Route(['GET'], '/posts/{slug:[a-z0-9-]+}')]    // Alphanumeric slug
#[Route(['GET'], '/files/{path:.+}')]             // Catch-all path

Architecture

VagKalosynakis\LeagueRouteAttributes\
├── Attributes/
│   ├── Route.php               # Route declaration
│   ├── Middleware.php          # Middleware application
│   └── WithoutMiddleware.php   # Middleware exclusion
└── Discovery/
    └── RouteDiscovery.php      # Route scanning and registration

PSR Compliance

  • PSR-1: Basic Coding Standard
  • PSR-4: Autoloading
  • PSR-12: Extended Coding Style
  • PSR-11: Container Interface
  • PSR-15: HTTP Handlers/Middleware
Testing

Running Tests

# Install dev dependencies
composer install

# Run test suite
composer test

# Run with coverage
composer test:coverage

# Static analysis
composer phpstan

# Code style check
composer cs:check

# Fix code style
composer cs:fix

Test Structure

tests/
├── Unit/              # Unit tests for attributes and discovery
└── Integration/       # Integration tests with league/route

Requirements for Testing

  • PHPUnit ^9.6
  • Mockery ^1.5
  • PHPStan ^1.10
  • PHP CS Fixer ^3.16

License

MIT License. See LICENSE file for details.

Contributing

Contributions welcome. Please follow PSR-12 coding standards and ensure tests pass before submitting PRs.

Credits

Built on top of league/route by The PHP League.