PHPackages                             poshtive/router - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [API Development](/categories/api)
4. /
5. poshtive/router

ActiveLaravel-package[API Development](/categories/api)

poshtive/router
===============

Convention-based route discovery for Laravel controllers using PHP attributes.

v1.2.0(2d ago)0405MITPHPPHP ^8.3CI passing

Since Mar 25Pushed 3mo agoCompare

[ Source](https://github.com/poshtive/laravel-router)[ Packagist](https://packagist.org/packages/poshtive/router)[ RSS](/packages/poshtive-router/feed)WikiDiscussions master Synced today

READMEChangelogDependencies (18)Versions (7)Used By (0)

Laravel Router
==============

[](#laravel-router)

Convention-based route discovery for Laravel controllers using PHP attributes.

Status
------

[](#status)

[![Tests](https://github.com/qoqn/laravel-router/actions/workflows/tests.yml/badge.svg?branch=master)](https://github.com/qoqn/laravel-router/actions/workflows/tests.yml)[![codecov](https://camo.githubusercontent.com/1acdb4f37757af9ef144a6bf4ad091fb4b349258c6c4d0262dc75b5c4abc8f06/68747470733a2f2f636f6465636f762e696f2f67682f716f716e2f6c61726176656c2d726f757465722f6272616e63682f6d61737465722f67726170682f62616467652e737667)](https://codecov.io/gh/qoqn/laravel-router)

`laravel-router` is stable and released as `1.0.0`.

- PHP: `^8.3`
- Laravel components: `^13.0`
- Test coverage: PHPUnit 11 + Orchestra Testbench 11, with `src/` at 100% locally

Highlights
----------

[](#highlights)

- Attribute-driven route discovery with minimal registration boilerplate
- Support for nested controller resources and model-bound parameters
- Convention-based HTTP verb resolution with optional explicit overrides
- Middleware, `where` constraints, and inheritance-aware discovery
- Strict duplicate detection and optional skipped-route reporting

Installation
------------

[](#installation)

You can install the package via Composer:

```
composer require poshtive/router
```

Optionally, you can publish the configuration file using:

```
php artisan vendor:publish --provider="Poshtive\Router\RouterServiceProvider" --tag="config"
```

Testing
-------

[](#testing)

Run the package test suite with:

```
composer test
```

Generate a Clover coverage report for `src/` with:

```
composer test:coverage
```

Configuration
-------------

[](#configuration)

After publishing, optionally edit `config/router.php`. Below are the available options.

### `convention` (string)

[](#convention-string)

Controls how controller methods become routes. Default is `attribute_or_get`.

- `attribute_or_get`: Plain method names are treated as routes converted to kebab-case with GET unless overridden with an attribute.

    Example:

    ```
    use Poshtive\Router\Attributes\Route;

    class UserController {
        public function index() {}            // GET /user

        #[Route(method: 'POST')]
        public function store() {}            // POST /user/store

        #[Route(method: ['PUT', 'PATCH'])]
        public function updateApp() {}        // PUT & PATCH /user/update-app
    }
    ```
- `prefix`: Method names start with the HTTP verb. Pattern: `{verb}{StudlyAction}` with action name will be converted to kebab-case. Example:

    ```
    class UserController {
        public function getIndex() {}          // GET /user
        public function postStore() {}         // POST /user/store
        public function deleteDestroyApp() {}  // DELETE /user/destroy-app
    }
    ```

### `method_extends` (bool)

[](#method_extends-bool)

Include methods inherited from parent classes. Default is `false`.

- `false`: Only the concrete controller’s own methods are scanned.
- `true`: Parent class methods are also evaluated (useful for shared CRUD bases).

Example:

```
use Poshtive\Router\Attributes\DoNotDiscover;

#[DoNotDiscover]
abstract class BaseCrudController {
    public function index() {}
}

class UserController extends BaseCrudController {
    public function show() {}
}
```

With `method_extends` = `true` both `index` and `show` register as `UserController` methods.

### `http_methods_map` (array)

[](#http_methods_map-array)

Available only when `convention` = `attribute_or_get`.

Maps method names to HTTP verbs when no attribute is present. Accepts string or array.

Example:

```
'http_methods_map' => [
    'store' => 'POST',
    'update' => ['PUT', 'PATCH'],
    'destroy' => 'DELETE',
],
```

Attribute precedence: If a `#[Route(method: ...)]` is present, it overrides this map.

### `report_skipped_routes` (bool)

[](#report_skipped_routes-bool)

Log intentionally skipped discovered methods, such as controllers marked with `#[DoNotDiscover]` or routes guarded by `#[LocalOnly]` outside the local environment. Default is `false`.

### `strict` (bool)

[](#strict-bool)

Fail route discovery when duplicate route names or duplicate `HTTP_VERB + URI` combinations are discovered. Default is `false`.

- `false`: Duplicate definitions are reported to the logger when available.
- `true`: Duplicate definitions throw an exception and stop registration.

### Sample Configuration

[](#sample-configuration)

```
return [
    'convention' => 'attribute_or_get',
    'method_extends' => false,
    'http_methods_map' => [
        'store' => 'POST',
        'update' => ['PUT', 'PATCH'],
        'destroy' => 'DELETE',
    ],
    'report_skipped_routes' => false,
    'strict' => false,
];
```

### Quick Decision Guide

[](#quick-decision-guide)

- Prefer `prefix` for explicitness and zero attributes.
- Prefer `attribute_or_get` for clean method names + selective attributes.
- Enable `method_extends` when using abstract/base controllers for shared actions.
- Populate `http_methods_map` to reduce repetitive attributes for common REST verbs.
- Enable `report_skipped_routes` while integrating the package into an existing app.
- Enable `strict` once your route structure is stable and you want duplicate discovery to fail fast.

Registering Routes
------------------

[](#registering-routes)

In your `routes/web.php`, add:

```
use Poshtive\Router\Router;

Router::create()->discover(app_path('Http/Controllers'));
```

Please note that you can still define routes manually as usual.

How It Works
------------

[](#how-it-works)

The package scans the specified directory for controller classes and their public methods. It then registers routes based on the chosen convention and any attributes applied to the classes or methods.

In general, the route path is constructed as follows:

```
/folder-one/folder-two/controller-name/{parameter-1}/method-name/{parameter-2}/...

```

With some exception, see [Parameter Order](#parameter-order) and [Child Controllers](#child-controllers).

Only `public` methods are considered. Methods inherited from parent classes are included only if `method_extends` is set to `true` in the configuration. All other methods are ignored.

Index Controller and Method
---------------------------

[](#index-controller-and-method)

Controller named `IndexController` will have its name omitted from the route path. Also, method named `index` will have its name omitted from the route path.

Example:

```
namespace App\Http\Controllers;

class IndexController {
    public function index() {}
    public function about() {}
}
```

Resulting route: `GET /` and `GET /about`.

```
namespace App\Http\Controllers;

class UserController {
    public function index() {}
    public function show() {}
}
```

Resulting route: `GET /user` and `GET /user/show`.

```
namespace App\Http\Controllers\Admin;

class IndexController {
    public function index() {}
    public function dashboard() {}
}
```

Resulting route: `GET /admin` and `GET /admin/dashboard`. And so on.

Parameter / Model Binding
-------------------------

[](#parameter--model-binding)

Method parameters are converted to route parameters in the order they appear in the method signature. Only primitive types (`int`, `string`) and classes extending `Illuminate\Database\Eloquent\Model` are considered.

Example:

```
use Poshtive\Router\Attributes\Route;
use App\Models\User;

class UserController {
    public function show(int $id) {}

    public function edit(User $user) {}
}
```

This results in:

- `GET /user/{id}/show` with `{id}` as an integer.
- `GET /user/{user}/edit` with `{user}` as a model instance.

Parameter Order
---------------

[](#parameter-order)

By default, parameters are ordered based on their appearance in the method signature.

```
class UserController {
    public function update(int $id, string $section) {}
}
```

Results in `GET /user/{id}/update/{section}`, please note that the `id` parameter is placed before the method name. You can override this behavior using `keepOrder: true` in the `Route` attribute:

```
use Poshtive\Router\Attributes\Route;

class UserController {
    #[Route(keepOrder: true)]
    public function update(int $id, string $section) {}
}
```

Results in `GET /user/update/{id}/{section}`.

Child Controllers
-----------------

[](#child-controllers)

When a controller name without the `Controller` suffix has a folder name matching it in the path, all the controllers inside that folder are treated as child controllers of that controller and the route registration is handled accordingly. See the example below for clarity.

Given the following structure:

```
app/Http/
└── Controllers/
    ├── UserController.php
    └── User/
        ├── ProfileController.php
        └── SettingsController.php

```

The routes will be registered as:

- `UserController` methods: `/user/{parameter}/method-name`
- `ProfileController` methods: `/user/{parameter1}/profile/{parameter2}/method-name`
- `SettingsController` methods: `/user/{parameter1}/settings/{parameter2}/method-name`

So, all the controller's methods inside the `User` folder must have at least one parameter that extends `Illuminate\Database\Eloquent\Model`. Registration will fail otherwise.

Available Attributes
--------------------

[](#available-attributes)

All attributes are in the `Poshtive\Router\Attributes` namespace.

### Route

[](#route)

Can be applied to `class` or `method`. Defines middleware, route path, HTTP method(s), and parameters order.

Note

Only `middleware` are effective on `class` level. Other options are ignored.

Example:

```
use Poshtive\Router\Attributes\Route;

#[Route(middleware: ['auth'])]
class UserController {
    #[Route(uri: 'profile', method: 'GET')]
    public function showProfile() {}

    #[Route(method: ['POST', 'PUT'], middleware: ['log'])]
    public function updateSection(int $id, string $section) {}

    #[Route(keepOrder: true)]
    public function customOrder(string $section, int $id) {}
}
```

This means:

- `GET /profile` with `auth` middleware.
- `POST` &amp; `PUT /user/{id}/update-section/{section}` with `auth` and `log` middleware.
- `GET /user/custom-order/{section}/{id}` with `auth` middleware, preserving parameter order.

For information why `keepOrder` is needed here, see this section about [Parameter Order](#parameter-order).

### LocalOnly

[](#localonly)

Can be applied to `class` or `method`. Marks the route as local-only, meaning it will only be registered when the application is running in a local environment.

Example:

```
use Poshtive\Router\Attributes\LocalOnly;

#[LocalOnly]
class UserController {
    public function index() {}
}
```

Means `GET /user` is only registered in local environment.

### DoNotDiscover

[](#donotdiscover)

Can be applied to `class`. Marks all the controller's methods to be ignored during route discovery.

Example:

```
use Poshtive\Router\Attributes\DoNotDiscover;

#[DoNotDiscover]
class UserController {
    public function index() {}
}
```

Means no routes from `UserController` will be registered.

Please note that the `Route` attribute if any, will still be effective to child classes.

Example:

```
use Poshtive\Router\Attributes\DoNotDiscover;

#[DoNotDiscover]
#[Route(middleware: ['auth'])]
class AuthenticatedConcerns extends Controller {
}

class UserController extends AuthenticatedConcerns {
    public function index() {}
}
```

Means `GET /user` will be registered with `auth` middleware even though `AuthenticatedConcerns` itself is marked as `DoNotDiscover`.

### Where

[](#where)

Can be applied to `method`. Defines regex constraints for route parameters.

Example:

```
use Poshtive\Router\Attributes\Where;

class UserController {
    #[Where('id', '\d+')]
    public function show(int $id) {}
}
```

Means `GET /user/{id}/show` will only match if `{id}` is numeric. If Multiple `Where` attributes are applied, all constraints must be satisfied.

```
use Poshtive\Router\Attributes\Where;

class UserController {
    #[Where('id', '\d+')]
    #[Where('slug', '[a-z0-9-]+')]
    public function show(int $id, string $slug) {}
}
```

### IgnoreParentMiddleware

[](#ignoreparentmiddleware)

Can be applied to `class` or `method`. When applied to a class, it prevents middleware defined in parent classes from being inherited. When applied to a method, it prevents middleware defined at the class level from being applied to that specific method. This includes middleware defined by parent classes.

Example:

```
use Poshtive\Router\Attributes\IgnoreParentMiddleware;

#[IgnoreParentMiddleware]
class UserController extends AuthenticatedConcerns {
    public function index() {}
}
```

Means `GET /user` will be registered without any middleware from `AuthenticatedConcerns` if any.

```
use Poshtive\Router\Attributes\IgnoreParentMiddleware;
use Poshtive\Router\Attributes\Route;

#[Route(middleware: ['auth'])]
class AuthenticatedConcerns extends Controller {}

class UserController extends AuthenticatedConcerns {
    #[IgnoreParentMiddleware]
    public function show() {}

    public function app() {}
}
```

Means `GET /user/show` will be registered without any middleware from `AuthenticatedConcerns`. But, `GET /user/app` will still have the `auth` middleware.

Contributing
------------

[](#contributing)

Contributions are welcome! Please feel free to submit issues or pull requests.

Changelog
---------

[](#changelog)

Release notes are tracked in [CHANGELOG.md](CHANGELOG.md).

Licensing
---------

[](#licensing)

This package is open-sourced software licensed under the [MIT license](LICENSE).

###  Health Score

44

—

FairBetter than 90% of packages

Maintenance89

Actively maintained with recent releases

Popularity16

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~58 days

Recently: every ~67 days

Total

6

Last Release

2d ago

Major Versions

0.1.2 → v1.0.02026-03-25

### Community

Maintainers

![](https://www.gravatar.com/avatar/527bcf0ae9008e26511af0ccc7fc1daa73c2358c36b0a05bb573459a6c8053b7?d=identicon)[moerqal](/maintainers/moerqal)

---

Top Contributors

[![qoqn](https://avatars.githubusercontent.com/u/22430343?v=4)](https://github.com/qoqn "qoqn (16 commits)")

---

Tags

laravellaravel-packagelaravelrouterroutingdiscoveryattributes

###  Code Quality

TestsPHPUnit

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/poshtive-router/health.svg)

```
[![Health](https://phpackages.com/badges/poshtive-router/health.svg)](https://phpackages.com/packages/poshtive-router)
```

###  Alternatives

[psalm/plugin-laravel

Psalm plugin for Laravel

3355.3M346](/packages/psalm-plugin-laravel)[laravel/folio

Page based routing for Laravel.

603583.7k33](/packages/laravel-folio)[laravel/mcp

Rapidly build MCP servers for your Laravel applications.

77022.3M151](/packages/laravel-mcp)[nuwave/lighthouse

A framework for serving GraphQL from Laravel

3.5k11.8M117](/packages/nuwave-lighthouse)[api-platform/laravel

API Platform support for Laravel

58171.6k14](/packages/api-platform-laravel)[illuminate/filesystem

The Illuminate Filesystem package.

16165.1M3.2k](/packages/illuminate-filesystem)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
