Luminova Framework

PHP Luminova: HTTP Controllers URI Prefix Attribute Class

Last updated: 2026-03-02 09:38:55

Luminova’s Prefix attribute allows you to group URI prefixes in the controller class level for routing and optionally assign an error handler or exclude prefixes for routing performance.

The Prefix attribute is a class-level routing attribute that assigns a URI prefix or pattern to a controller.It groups related routes under one controller and prevents route overlap.

In practice, it serves the same organizational purpose as grouping routes in a file such as /routes/web.php or using context() in the front controller /public/index.php, but it keeps routing logic closer to the controller itself.

Using a prefix keeps your routes organized and improves performance.The router knows exactly which controller should handle a request, so it resolves routes faster during attribute scanning and caching.

Note:

A controller prefix does not automatically prepend itself to method-level route definitions.Each method using Luminova\Attributes\Route must still define its full URI path, including the base-prefix when required.


Supported Pattern Types

Prefix patterns support multiple formats:

  • Static paths such as (e.g, /api, /admin, /panel)
  • Regular expressions for advanced matching and exclusions (e.g, /admin/([^*]))
  • Dynamic placeholders for flexible URI patterns (e.g. (:root), (:base))

Relationship to Method-Based Routing

Prefix-based routing is a structured alternative to method-based route grouping.

Instead of organizing routes in external files, the controller itself defines its routing scope when attribute routing is enabled.This improves readability, reduces duplication, and keeps routing behavior close to the code it affects.


Examples

Single Controller for Web Routes

Use one controller to handle all standard website routes while excluding API endpoints.

This setup keeps web pages and API routes separated.

// /app/Controllers/Http/AppController.php

namespace App\Controllers\Http;

use Luminova\Base\Controller;
use Luminova\Attributes\Prefix;

#[Prefix(pattern: '/(?!api).*')]
class AppController extends Controller
{
   // Handles all routes except those starting with /api
}

Behavior

  • Matches /, /about, /products, etc.
  • Does NOT match /api/...

Grouping API Routes

Assign a controller to handle every route that begins with /api.

// /app/Controllers/Http/ResetController.php

namespace App\Controllers\Http;

use Luminova\Base\Controller;
use Luminova\Attributes\Prefix;

#[Prefix(pattern: '/api/(:base)')]
class ResetController extends Controller
{
    // Handles all routes that begin with /api
}

Behavior

  • Matches /api/users, /api/auth/login, etc.
  • Keeps API logic separate from web controllers

URI Segment Exclusion

Luminova supports excluding URI segments to prevent route conflicts and improve routing performance.

Exclusions can be applied during attribute parsing.


Attribute-Level Exclusion

Define a base prefix and list segments that should be ignored.

// /app/Controllers/Http/MainController.php

namespace App\Controllers\Http;

use Luminova\Base\Controller;
use Luminova\Attributes\Prefix;

#[Prefix('/', exclude: ['api', 'blog', 'admin'])]
class MainController extends Controller
{
    // Handles all routes except:
    // /api/*
    // /blog/*
    // /admin/*
}
// /app/Controllers/Http/BlogController.php

namespace App\Controllers\Http;

use Luminova\Base\Controller;
use Luminova\Attributes\Prefix;

#[Prefix('/blog/(:base)', exclude: ['api', 'admin'])]
class BlogController extends Controller
{
    // Handles /blog/*
    // Excludes:
    // /blog/api/*
    // /blog/admin/*
}

Nested Segment Exclusions

You can split routing across multiple controllers based on URI exclusions and depth.

#[Prefix('/a/(:base)', exclude: ['b'])]
class A extends Controller
{
    // Handles /a/*
    // Excludes /a/b/*
}

#[Prefix('/a/b/(:base)', exclude: ['c'])]
class B extends Controller
{
    // Handles /a/b/*
    // Excludes /a/b/c/*
}

// May not behave as expected when attribute caching is enabled
#[Prefix('/a/b/c/(:base)')]
class C extends Controller
{
    // Handles /a/b/c/*
}

Guidelines

  • Nested exclusions are unlimited
  • For best reliability, keep nesting to 3 levels or fewer when attribute caching is enabled

Custom URI Segment Exclusion

Defining a custom regex pattern that excludes specific URI segments directly in the prefix.

This approach enforces stricter matching during route resolution and can improve performance because unwanted routes are rejected immediately.

Suitable when:

  • You need strict control over matching behavior
  • You want validation handled entirely by custom regex
  • You want early rejection of unwanted routes
// /app/Controllers/Http/MainController.php

namespace App\Controllers\Http;

use Luminova\Base\Controller;
use Luminova\Attributes\Prefix;

#[Prefix(pattern: '/(?!api(?:/|$)|blog(?:/|$)|admin(?:/|$)).*')]
class MainController extends Controller
{
    // Handles all routes except:
    // /api/*
    // /blog/*
    // /admin/*
}

Important Note:

Do not combine:

  • pattern exclusions, and
  • exclude arrays

Using both in the same attribute can cause conflicting matches and unpredictable routing behavior.

Pick one strategy and stick to it.


Pattern Placeholders

Pattern placeholders allows you to reuse a complex regex definitions across controllers. This keeps controller attributes readable and reduces duplication. For more information see Luminova\Routing\Router::pattern, documentation.


Defining a Placeholder

Register a reusable pattern during application startup.

// /app/Application.php

namespace App;

use Luminova\Routing\Router;

class Application extends Luminova\Foundation\Core\Application
{
   protected function onCreate(): void
   {
      Router::pattern('home', '(?!api(?:/|$)|blog(?:/|$)|admin(?:/|$)).*');
   }
}

This creates a reusable placeholder named home that matches all routes except:

  • /api/*
  • /blog/*
  • /admin/*

Using the Placeholder

Apply the registered pattern inside a controller prefix.

// /app/Controllers/Http/MainController.php

namespace App\Controllers\Http;

use Luminova\Base\Controller;
use Luminova\Attributes\Prefix;

#[Prefix(pattern: '/(:home)')]
class MainController extends Controller
{
   // Handles all non-API, non-blog, non-admin routes
}

Prefix Error Handler

You can assign a custom error handler to a prefix so every route under that controller shares the same fallback behavior.

This is useful for grouped areas like admin panels, APIs, or modules that require consistent error handling.

// /app/Controllers/Http/AdminController.php

namespace App\Controllers\Http;

use Luminova\Base\Controller;
use Luminova\Attributes\Prefix;
use App\Errors\Controllers\ErrorController;

#[Prefix(
    pattern: '/admin/(:base)',
    onError: [ErrorController::class, 'onAdminError']
)]
class AdminController extends Controller
{
    // Handles all /admin/* routes
    // Failed route resolution triggers onAdminError()
}

The prefix /admin/(:base) groups all admin routes under this controller.

  • If a route cannot be resolved (missing URI, method not found, etc.), the defined error handler runs automatically.
  • Error handling remains scoped to the prefix instead of affecting the entire application.

Class Definition

  • Class namespace: Luminova\Attributes\Prefix
  • This class is marked as final and can't be subclassed

Methods

constructor

Defines a non-repeatable routing prefix for HTTP controller classes.

The attribute assigns a URI prefix to a controller and optionally defines:

  • a routing error handler
  • URI prefixes to exclude from matching
  • whether exclusions should be merged into the pattern

It helps organize controllers and improves routing performance when attributes are compiled.

public __construct(
   string $pattern,
   string|array|null $onError = null,
   array $exclude = [],
   bool $mergeExcluders = false
)

Parameters:

ParameterTypeDescription
$patternstringThe URI prefix or pattern handled by the controller. Examples: /user, /user/(:base), /admin/(:root).
$onErrorarray|string|nullOptional routing error handler. Can be a callable or [ClassName::class, 'method'].
$excludearrayList of URI segments that should not match this controller.
Used during attribute parsing to prevent route conflicts.
$margeExcludersboolWhen true, exclusions are merged into the pattern as a single regex. When false, exclusions are processed separately (default: false).

Throws

Note:

Only one Prefix attribute can be be assigned to a controller.And you can optionally define error handler without needing Error attribute class.