laravel5 route changed to rule matching

Keywords: PHP Laravel Database

laravel has many projects in use, but routing configuration is an indispensable work. In many cases, routing configuration not only increases the repetitive work, but also increases the code overhead. When the project reaches a certain level, the number of routing configurations will be amazing.

In many cases, we have to think that although the laravel routing configuration is flexible but bloated, it is more convenient to use other routing frameworks that can automatically match. When the route in the project is by namespace \ class name + method name, the general configuration method can be used to simplify the route configuration.

If laravel uses auto match routing, will it lose the function or feature of the framework itself? This is determined by the user or decision-maker. If too many routes are considered acceptable, this is not necessary.

When you need this configuration, you can use the following code block directly.

The route configuration code is as follows: (this code needs to be configured after other routes, otherwise it may affect other route matching)

    //All other routes cannot be added after this one
    Route::any('/{controller}.{action}/{one?}/{two?}/{three?}/{four?}/{five?}', [
                'as' => 'mvc',
                'uses' => function() {
                    return abort(404);
                }])
            ->where('controller', '(.*)');
    //Event handling after matching mvc successfully
    Route::matched(function(\Illuminate\Routing\Events\RouteMatched $matched) {
        if ($matched->route->getName() == 'mvc') {
            $controllerParam = $matched->route->parameter('controller');
            $actionParam = $matched->route->parameter('action');
            //Must be legal
            if (!preg_match('#[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*(/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)+#', $controllerParam . '/' . $actionParam)) {
                return;
            }
            //Controller and method analysis match
            $space = explode('/', $controllerParam);
            $namecpase = $matched->route->getAction('namespace') ?: 'App\\Http\\Controllers';
            $controller = rtrim($namecpase, '\\') . '\\' . implode('\\', array_map('studly_case', array_filter($space))) . 'Controller';
            $action = studly_case($actionParam);
            if (!method_exists($controller, $action)) {
                return;
            }
            $method = new ReflectionMethod($controller, $action);
            $comment = $method->getDocComment();
            if(!$method->isPublic() || !preg_match('/@methods\(\s*([a-z]+)(\s*,\s*[a-z]+)\s*\)/i', $comment, $matches)){
                return;
            }
            $methods = array_map(function($item) {
                return trim(ltrim($item, ','));
            }, array_slice($matches, 1));
            if(!in_array(request()->method(), $methods)){
                return;
            }
            //Route parameter processing
            $parameters = array_keys($matched->route->parameters());
            $parameters = array_slice($parameters, array_search('one', $parameters));
            $setParameters = [];
            foreach ($method->getParameters() as $num => $parameter) {
                $class = $parameter->getClass();
                if ($class && is_subclass_of($class->name, Illuminate\Database\Eloquent\Model::class) && !is_null($matched->route->parameters[$parameters[$num]])) {
                    $setParameters[$parameter->name] = $matched->route->parameters[$parameters[$num]];
                }
            }
            $matched->route->parameters = array_merge($setParameters, $matched->route->parameters);
            $actions = $matched->route->getAction();
            $actions['uses'] = $controller . '@' . $action;
            $actions['controller'] = $actions['uses'];
            $matched->route->setAction($actions);
        }
    });

Controller configuration code:

namespace App\Http\Controllers\IndexController;

class IndexController extends Controller {
    /**
     * Initialize, configure Middleware
     */
     public function __construct() {
        $this->middleware(['auth'], ['only' => ['modify'], 'except' => ['add']]);
    }

    /**
     * list
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @methods(POST, GET)
     */
    public function lists() {
              return view('index.lists', ['lists' => Model::paginate()]);
        }
         /**
     * list
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @methods(GET)
     */
        public function add() {
                return view('index.add', []);
        }
         /**
     * list
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @methods(GET)
     */
        public function modify(Model $model){
                return view('index.modify', ['model' => $model]);
        }

The access addresses are:
http://localhost/index.lists
http://localhost/index.add
http://localhost/index.modify/{model_id}

It should be noted that this configuration mode optimizes the routing performance and reduces the routing configuration work, but it will directly expose the relative path of the controller (it is not recommended to use it if it is biased). At the same time, it will also affect the function of getting the URL through the route, such as the route() function, which needs to pass the relative path of the controller. It is not good to specify other aliases (of course, this effect is good).

Precautions for use:
1. Non general middleware configuration needs to be configured in the constructor of the controller, or the function name used or excluded can be specified, such as the code above
2. The controller method can support 5 routing parameters. The parameters start after {action}. You can freely configure the parameter types
3. The request types allowed by the controller method are configured by annotation @ methods(POST, GET) can configure the desired corresponding request types, not configured, not accessible, and the method is a common method
4. If you need to generate a route address through the route() function, you need to specify the corresponding path parameters, such as route ('mvc ', ['controller' = >'index ','action' = >'lists'], or encapsulate them separately
5. The controller and method are strictly matched with legal identifiers, and those that can't be matched can be directly 404. Those with good security and prejudice can be modified by themselves
6. This configuration can exist at the same time as the standard configuration, but the rules do not overlap. In theory, this configuration will be better in the end
7. This configuration can be derived from domain name parameters or other parameters, depending on the requirements of the project

Posted by ben2468 on Mon, 11 May 2020 00:57:47 -0700