Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Simple Router Laravel Package

developermarius/simple-router

A lightweight, easy-to-use router for Laravel and PHP applications. Define clean routes with minimal configuration, map URLs to controllers or closures, and keep setup simple—ideal for small projects or developers who want straightforward routing without extra complexity.

View on GitHub
Deep Wiki
Context7

Getting Started

Install via Composer and point a single front controller at it — the package is designed to drop into any existing project without a framework or container.

composer require developermarius/simple-router

Look first at: the README.md for the route-definition syntax, then src/ to confirm the exact dispatch method name (run, dispatch, or similar) and how the router exposes the current request. Because this is a small, low-star package, the source is the real documentation — it is short enough to read end to end in one sitting, and that is worth doing before wiring it into anything non-trivial.

Minimal setup (public/index.php):

require __DIR__ . '/../vendor/autoload.php';

$router = new \DeveloperMarius\SimpleRouter\Router();

$router->get('/', fn() => 'Hello world');

$router->get('/hello/{name}', function ($name) {
    return "Hello, {$name}";
});

$router->run();

Route everything through it in your web server config (Apache .htaccess rewrite or the nginx try_files equivalent) so that paths reach index.php intact.

First meaningful use case: a small JSON API or a handful of marketing pages inside a legacy PHP app. The router maps cleanly onto "one file per concern" — you add routes in index.php, return arrays/strings from closures, and progressively move handlers into controller classes as the file grows. That incremental path is the whole point of the package: it is a routing layer, not a framework, so you adopt only the piece you need.

Implementation Patterns

Closures for prototypes, controllers for anything lasting. Start with inline closures, then promote to [HomeController::class, 'index'] or invokable classes once you have more than a screenful of routes. Controller methods typically receive route parameters as positional arguments, so keep signatures aligned with the {param} order in the path.

Parameterised routes with constraints. Use {id} placeholders and add a per-route pattern where the router supports it (e.g. ->where('id', '[0-9]+')) to avoid your controller doing integer validation. Route order matters — declare /users/new before /users/{id} or the dynamic route will swallow it.

Route groups for shared prefixes and middleware. Group a REST resource under a prefix and attach middleware once:

$router->group(['prefix' => '/api/v1', 'middleware' => [AuthMiddleware::class]], function ($router) {
    $router->get('/users', [UserController::class, 'index']);
    $router->post('/users', [UserController::class, 'store']);
});

Middleware as a chain. Middleware in a Laravel-inspired router is usually a callable receiving the request and a $next continuation. Keep them single-purpose: authentication, JSON content negotiation, CORS headers, rate limiting. Register global middleware for cross-cutting concerns and per-group middleware for auth scoping — this is where the Laravel lineage pays off, because the mental model transfers directly.

Named routes and URL generation. If the package exposes ->name('users.show') and a route()/url() helper, use names everywhere instead of hardcoded paths. It is the cheapest refactor insurance you will get in a router this size, and it keeps redirects and views consistent when prefixes change.

Integration tips:

  • Pair with a PSR-7 implementation only if you actually need immutable request/response objects; for most small apps, plain $_SERVER/$_GET access inside handlers is fine and faster to write.
  • Keep handler return values predictable (string, array, or a Response object) and let a thin wrapper do the JSON encoding — do not encode in every controller.
  • Put the router behind a bootstrap.php that builds shared services (DB connection, config array) and passes them into controllers via constructor or a tiny container closure. The router itself does not manage dependencies; you supply that discipline.

Gotchas and Tips

Trailing-slash and subdirectory behaviour. Small routers frequently treat /about and /about/ as distinct, and break when the app lives in a subdirectory rather than at the domain root. Decide the convention once, test both forms, and if there is a base-path option in config, set it in one place rather than prefixing every route by hand.

405 vs 404 ambiguity. Many minimal routers return 404 for a path that exists under a different HTTP verb. If the package does not distinguish, add a small pre-dispatch table of known paths per method, or accept the ambiguity knowingly. This bites API consumers who send POST to a PUT route.

First-match-wins routing. Static-before-dynamic is the rule of thumb; group your definitions accordingly. When a request silently hits the wrong handler, log the matched route pattern (not just the URI) during development — it turns a ten-minute puzzle into a five-second one.

Method spoofing is not automatic. HTML forms cannot send PUT/PATCH/DELETE. If you use RESTful verbs, add a _method field and a middleware that rewrites the method before routing, or just use POST routes for forms and PUT/DELETE only in APIs.

Autoloading and namespaces in controller strings. [UserController::class, 'index'] resolves at compile time and is safe; the 'UserController@index' string form requires the router to prefix a namespace. Confirm which convention the package expects before scattering string references — mismatches surface as "callable not found" at runtime, often only on the route you did not click during testing.

Debugging tips. Dump the registered route table during development (iterate the router's route collection) to verify patterns compiled as expected. For 404s, temporarily disable middleware to isolate whether the failure is routing or auth. For mysterious "headers already sent" errors, check that handlers do not echo before returning and that no whitespace precedes <?php in included files.

Extension points. The natural seams are: custom middleware (register global before per-route), a custom not-found handler so you control whether it returns HTML or JSON, and a response-transforming wrapper around your controller return values. If the package allows it, wrap run() in your own dispatcher so you can add timing, logging, and exception-to-HTTP-status mapping in a single place — this is the one piece of infrastructure worth building early, because it is what lets you keep every controller a pure function of its inputs.

Version pinning. Last release is recent but the project is small with no dependents; pin a minor version and skim the changelog before upgrades, since pre-1.0-style packages often make breaking changes to dispatch and middleware signatures without fanfare. If you need long-term stability, keep an integration test that exercises one route per HTTP verb through the full stack.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
cgv/localized-resource