laravel5路由改爲規則匹配

laravel已經有很多項目在使用,但是路由配置卻是一個不可缺少的工作,很多時候路由配置工作不僅增加重複工作量而且還會增加代碼開銷,當項目到一定的級別時路由配置數量將是驚人的。

很多時候不得不認爲laravel路由配置雖然靈活但很臃腫,反而使用其它可以自動匹配的路由的框架更方便。當項目中路由是按 命名空間\類名+方法名 時就可以使用通用的配置方式來簡化路由配置。

如果laravel使用自動匹配路由是否會折損框架本身的功能或特性麼?這是由使用人或決策人來判定的,如果認爲路由過多是可以接受的那麼則不需要這麼操作。

當如果需要這麼配置時,可以直接使用下面的代碼塊。

路由配置代碼如下:(這塊代碼需要配置到其它路由後面,否則可能會影響其它路由匹配)

    //所有其它路由不可在這條之後添加
    Route::any('/{controller}.{action}/{one?}/{two?}/{three?}/{four?}/{five?}', [
                'as' => 'mvc',
                'uses' => function() {
                    return abort(404);
                }])
            ->where('controller', '(.*)');
    //匹配mvc成功後事件處理
    Route::matched(function(\Illuminate\Routing\Events\RouteMatched $matched) {
        if ($matched->route->getName() == 'mvc') {
            $controllerParam = $matched->route->parameter('controller');
            $actionParam = $matched->route->parameter('action');
            //必需合法
            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;
            }
            //控制器與方法解析匹配
            $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;
            }
            //路由參數處理
            $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);
        }
    });

控制器配置代碼:

namespace App\Http\Controllers\IndexController;

class IndexController extends Controller {
    /**
     * 初始化,配置中間件
     */
     public function __construct() {
        $this->middleware(['auth'], ['only' => ['modify'], 'except' => ['add']]);
    }

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

訪問地址分別是:
http://localhost/index.lists
http://localhost/index.add
http://localhost/index.modify/{model_id}

需要注意的是,這種配置方式優化了路由性能,減少了路由配置工作,但會直接暴露控制器的相對路徑(對此有偏見的不建議使用),同時也會影響通過路由獲取URL的功能,比如route()函數,需要通過控制器的相對路徑,不好再指定其它別名(當然這塊影響還好)。

使用注意事項:
1、非通用的中間件配置需要放到控制器的構造函數來配置,也可以指定使用或排除的函數名,如上面的代碼中
2、控制器方法可以支持到5個路由參數,參數是以{action}之後開始,可以自由配置參數類型
3、控制器方法允許的請求類型通過註釋來配置@methods(POST, GET) 可以配置想要的對應請求類型,不配置不能訪問,並且方法是共有方法
4、如果需要通過route()函數生成路由地址,需要指定對應的路徑參數,如:route('mvc',['controller'=>'index','action'=>'lists']) 或者另外封裝
5、控制器與方法是嚴格匹配合法標識符,匹配不到的直接404,安全性好,有偏見的可以自行修改
6、這種配置可以與標準配置同時存在,只是規則不重疊即可,理論上這種配置放到最後會更好
7、這種配置可以派生到域名參數,或其它參數上,看項目的需求

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章