Routing

Default Routing Rules

The default routing rule of Webman is http://127.0.0.1:8787/{controller}/{action}.

The default controller is app\controller\IndexController, and the default action is index.

For example, visiting:

  • http://127.0.0.1:8787 will default to calling the index method of the app\controller\IndexController class
  • http://127.0.0.1:8787/foo will default to calling the index method of the app\controller\FooController class
  • http://127.0.0.1:8787/foo/test will default to calling the test method of the app\controller\FooController class
  • http://127.0.0.1:8787/admin/foo/test will default to calling the test method of the app\admin\controller\FooController class (see Multiple Applications)

Starting from version 1.4, Webman also supports more complex default routing, for example:

app
├── admin
│   └── v1
│       └── v2
│           └── v3
│               └── controller
│                   └── IndexController.php
└── controller
    ├── v1
    │   └── IndexController.php
    └── v2
        └── v3
            └── IndexController.php

If you want to change a specific request route, please modify the configuration file config/route.php.

If you want to disable the default routing, add the following configuration to the last line of the configuration file config/route.php:

Route::disableDefaultRoute();

Closure Routes

Add the following routing code to config/route.php:

use support\Request;
Route::any('/test', function (Request $request) {
    return response('test');
});

Note
Since closure functions do not belong to any controller, $request->app, $request->controller, and $request->action will all be empty strings.

When accessing the address http://127.0.0.1:8787/test, it will return the string test.

Note
The route path must start with a /, for example:

use support\Request;
// Incorrect usage
Route::any('test', function (Request $request) {
    return response('test');
});

// Correct usage
Route::any('/test', function (Request $request) {
    return response('test');
});

Class Routes

Add the following routing code to config/route.php:

Route::any('/testclass', [app\controller\IndexController::class, 'test']);

When accessing the address http://127.0.0.1:8787/testclass, it will return the return value of the test method of the app\controller\IndexController class.

Route Parameters

If there are parameters in the route, they can be matched using {key}, and the matching results will be passed to the corresponding controller method parameters (starting from the second parameter in turn), for example:

// Matches /user/123 and /user/abc
Route::any('/user/{id}', [app\controller\UserController::class, 'get']);
namespace app\controller;
use support\Request;

class UserController
{
    public function get(Request $request, $id)
    {
        return response('Received parameter ' . $id);
    }
}

More examples:

use support\Request;
// Matches /user/123, does not match /user/abc
Route::any('/user/{id:\d+}', function (Request $request, $id) {
    return response($id);
});

// Matches /user/foobar, does not match /user/foo/bar
Route::any('/user/{name}', function (Request $request, $name) {
    return response($name);
});

// Matches /user, /user/123, and /user/abc. [] means optional.
Route::any('/user[/{name}]', function (Request $request, $name = null) {
    return response($name ?? 'tom');
});

// Matches any request with /user/ as a prefix.
Route::any('/user/[{path:.+}]', function (Request $request) {
    return $request->path();
});

// Matches all options requests. The pattern after the colon specifies the regular expression for this named parameter.
Route::options('[{path:.+}]', function () {
    return response('');
});

Summary of advanced usage:

The [] syntax in Webman routing is mainly used to handle optional path segments or match dynamic routes. It allows you to define more complex path structures and matching rules for routes.

The : is used to specify a regular expression.

Route Groups

Sometimes routes contain a large number of identical prefixes, wherein we can use route groups to simplify the definitions. For example:

Route::group('/blog', function () {
   Route::any('/create', function (Request $request) {return response('create');});
   Route::any('/edit', function (Request $request) {return response('edit');});
   Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
});

This is equivalent to

Route::any('/blog/create', function (Request $request) {return response('create');});
Route::any('/blog/edit', function (Request $request) {return response('edit');});
Route::any('/blog/view/{id}', function (Request $request, $id) {return response("view $id");});

Nested group usage:

Route::group('/blog', function () {
   Route::group('/v1', function () {
      Route::any('/create', function (Request $request) {return response('create');});
      Route::any('/edit', function (Request $request) {return response('edit');});
      Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
   });  
});

Route Middleware

We can set middleware for a specific route or a group of routes. For example:

Route::any('/admin', [app\admin\controller\IndexController::class, 'index'])->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);

Route::group('/blog', function () {
   Route::any('/create', function () {return response('create');});
   Route::any('/edit', function () {return response('edit');});
   Route::any('/view/{id}', function ($request, $id) {return response("view $id");});
})->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);
# Incorrect usage example (this usage is valid when webman-framework >= 1.5.7)
Route::group('/blog', function () {
   Route::group('/v1', function () {
      Route::any('/create', function (Request $request) {return response('create');});
      Route::any('/edit', function (Request $request) {return response('edit');});
      Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
   });  
})->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);
# Correct usage example
Route::group('/blog', function () {
   Route::group('/v1', function () {
      Route::any('/create', function (Request $request) {return response('create');});
      Route::any('/edit', function (Request $request) {return response('edit');});
      Route::any('/view/{id}', function (Request $request, $id) {return response("view $id");});
   })->middleware([
        app\middleware\MiddlewareA::class,
        app\middleware\MiddlewareB::class,
    ]);  
});

Resource Routes

Route::resource('/test', app\controller\IndexController::class);

// Specify resource routes
Route::resource('/test', app\controller\IndexController::class, ['index','create']);

// Non-defined resource routes
// For example, accessing notify can be either any route /test/notify or /test/notify/{id}, and routeName is test.notify
Route::resource('/test', app\controller\IndexController::class, ['index','create','notify']);
Verb URI Action Route Name
GET /test index test.index
GET /test/create create test.create
POST /test store test.store
GET /test/{id} show test.show
GET /test/{id}/edit edit test.edit
PUT /test/{id} update test.update
DELETE /test/{id} destroy test.destroy
PUT /test/{id}/recovery recovery test.recovery

URL Generation

Note
Currently, URL generation for nested group routes is not supported.

For example, given the route:

Route::any('/blog/{id}', [app\controller\BlogController::class, 'view'])->name('blog.view');

We can use the following method to generate the URL for this route:

route('blog.view', ['id' => 100]); // Result will be /blog/100

When using the route's URL in views, this method can be used so that no matter how the routing rules change, the URL will be auto-generated, avoiding a large number of changes to view files due to routing address adjustments.

Getting Route Information

We can obtain the current request route information through the $request->route object, for example:

$route = $request->route; // Equivalent to $route = request()->route;
if ($route) {
    var_export($route->getPath());
    var_export($route->getMethods());
    var_export($route->getName());
    var_export($route->getMiddleware());
    var_export($route->getCallback());
    var_export($route->param());
}

Note
If the current request does not match any routes configured in config/route.php, then $request->route will be null, which means that during default routing, $request->route will also be null.

Handling 404 Errors

When a route cannot be found, it defaults to returning a 404 status code and outputting 404-related content.

If developers want to intervene in the business process when a route is not found, they can use the fallback route provided by Webman Route::fallback($callback) method. For example, the following logic redirects to the homepage when a route is not found.

Route::fallback(function(){
    return redirect('/');
});

Or when a route does not exist, return a JSON response, which is very useful when Webman is used as an API.

Route::fallback(function(){
    return json(['code' => 404, 'msg' => '404 not found']);
});

Adding Middleware to 404

By default, 404 requests do not run any middleware. To add middleware to 404 requests, please refer to the following code.

Route::fallback(function(){
    return json(['code' => 404, 'msg' => '404 not found']);
})->middleware([
    app\middleware\MiddlewareA::class,
    app\middleware\MiddlewareB::class,
]);

Related link Custom 404 500 pages

Disabling Default Routes

// Disable the main project's default routes without affecting application plugins
Route::disableDefaultRoute();
// Disable the routes of the admin application of the main project, without affecting application plugins
Route::disableDefaultRoute('', 'admin');
// Disable the default routes of the foo plugin without affecting the main project
Route::disableDefaultRoute('foo');
// Disable the default routes of the admin application of the foo plugin without affecting the main project
Route::disableDefaultRoute('foo', 'admin');
// Disable the default route for the controller [\app\controller\IndexController::class, 'index']
Route::disableDefaultRoute([\app\controller\IndexController::class, 'index']);

Annotation to Disable Default Routes

We can disable the default routing for a specific controller through annotations, for example:

namespace app\controller;
use support\annotation\DisableDefaultRoute;

#[DisableDefaultRoute]
class IndexController
{
    public function index()
    {
        return 'index';
    }
}

Similarly, we can also disable the default routing for a specific method in a controller through annotations, for example:

namespace app\controller;
use support\annotation\DisableDefaultRoute;

class IndexController
{
    #[DisableDefaultRoute]
    public function index()
    {
        return 'index';
    }
}

Route Interface

// Set the route for any method request for $uri
Route::any($uri, $callback);
// Set the route for get requests for $uri
Route::get($uri, $callback);
// Set the route for post requests for $uri
Route::post($uri, $callback);
// Set the route for put requests for $uri
Route::put($uri, $callback);
// Set the route for patch requests for $uri
Route::patch($uri, $callback);
// Set the route for delete requests for $uri
Route::delete($uri, $callback);
// Set the route for head requests for $uri
Route::head($uri, $callback);
// Set the route for options requests for $uri
Route::options($uri, $callback);
// Simultaneously set routes for multiple types of requests
Route::add(['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'], $uri, $callback);
// Group routes
Route::group($path, $callback);
// Resource routes
Route::resource($path, $callback, [$options]);
// Disable routes
Route::disableDefaultRoute($plugin = '');
// Fallback route, set the default routing fallback
Route::fallback($callback, $plugin = '');
// Get all route information
Route::getRoutes();

If no corresponding route exists for the uri (including the default route), and the fallback route is not set, a 404 will be returned.

Multiple Route Configuration Files

If you want to manage routes with multiple configuration files, for example, when using Multiple Applications where each application has its own route configuration, you can load external route configuration files by requiring them.
For example, in config/route.php:

<?php

// Load route configurations under the admin application
require_once app_path('admin/config/route.php');
// Load route configurations under the api application
require_once app_path('api/config/route.php');