poshtive / router
Installs: 45
Dependents: 0
Suggesters: 0
Security: 0
Stars: 0
Watchers: 0
Forks: 0
Open Issues: 0
Type:laravel-package
pkg:composer/poshtive/router
Requires
- php: ^8.3
- illuminate/contracts: ^12.0
- illuminate/pipeline: ^12.0
- illuminate/routing: ^12.0
- illuminate/support: ^12.0
- symfony/finder: ^7.0
This package is auto-updated.
Last update: 2025-10-08 04:46:52 UTC
README
This repository contains my personal automatic router for Laravel. It's a project I've been working on to automate routing based on my own development preferences. If you're interested, you're welcome to use it.
Status and Inspiration
This is currently a personal project and is a work in progress. It was inspired by similar packages, particularly those from Spatie, a company known for its excellent Laravel packages.
Warning
Please note that this package may have breaking changes and is not guaranteed to be compatible with older versions of Laravel. It's highly recommended that you pin the package version in your composer.json
file. For now, the version is set below 1.0
.
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"
Configuration
After publishing, optionally edit config/router.php
. Below are the available options.
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)
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)
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.
Sample Configuration
return [ 'convention' => 'attribute_or_get', 'method_extends' => false, 'http_methods_map' => [ 'store' => 'POST', 'update' => ['PUT', 'PATCH'], 'destroy' => 'DELETE', ], ];
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.
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
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 and 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
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
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
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
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
All attributes are in the Poshtive\Router\Attributes
namespace.
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
withauth
middleware.POST
&PUT /user/{id}/update-section/{section}
withauth
andlog
middleware.GET /user/custom-order/{section}/{id}
withauth
middleware, preserving parameter order.
For information why keepOrder
is needed here, see this section about Parameter Order.
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
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
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
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
Contributions are welcome! Please feel free to submit issues or pull requests.
Licensing
This package is open-sourced software licensed under the MIT license.