Has anybody implement middleware for router? like :
$router->addx('/standards/add','standards::add',[middleware::class])->setName('standards.add');
|
Mar '16 |
4 |
1409 |
0 |
For Application there is https://docs.phalcon.io/pl/latest/reference/dispatching.html#dispatch-loop-events
After some digging and trying, I end up with myRouter having middleware manipulating and model binding.
... ...
$router->addMiddlewaresForEveryRoute([isLoggedin::class]);
$router->addx('/standards/{file:[0-9]+}/edit','standards::edit',[standardRules::class])->setName('standards.edit');
... ...
For the standardsController, the edit function could set parameters as follow:
public function editAction(Files $file)
{
$fileable = $file->getFileable();
... ...
$this->view->file = $file;
$this->view->form = myForm::buildFormFromModel($fileable,$extraData);
}
And middleware like:
class standardRules extends myValidationRules
{
public $rules = [
'title'=>['validator'=>'PresenceOf','message'=>'评论内容不能为空!']
];
}
myRouter is :
use Phalcon\Http\Request;
use Phalcon\Http\Response;
use Phalcon\Mvc\Dispatcher;
use Phalcon\Mvc\Router;
/**
* Created by PhpStorm.
* User: ThinkPad
* Date: 2015/3/21
* Time: 9:30
* 核心功能如下:
* 1、中间件的实现
* 2、模型绑定,model binding的实现,可以在action函数中指定绑定的类型,类似laravel 5.2 提供的一样
* 3、模型绑定基础上,实现接口绑定
*/
class myRouter extends Router{
/**
* @var array
*/
public $middlewares = [];
public $middlewaresForEveryRoute = [];
public $serviceProvider = [];
/**
* myRouter constructor.
*/
public function __construct($defaultRoutes = true)
{
parent::__construct($defaultRoutes);
// ---------解决中文url不稳定的问题----------
$this->setUriSource(Router::URI_SOURCE_SERVER_REQUEST_URI);//这种形式对比$_GET('_url')的要稳定,这个函数没有urldecode(),需要手动执行
$_SERVER['REQUEST_URI'] = urldecode($_SERVER['REQUEST_URI']);
// ---------解决中文url不稳定的问题----------
}
public function addMiddlewaresForEveryRoute(array $middleware=[])
{
$this->middlewaresForEveryRoute = $middleware;
}
/**
* 主要是增加了一个中间件的功能,利用short syntax来增加中间件,这样的好处是路由、中间件在一起,便于管理
* @param $pattern
* @param string $path
* @param array $middleware
* @return \Phalcon\Mvc\Router\Route
*/
public function addx($pattern,$path,array $middleware=[])//给路由添加中间件
{
$this->middlewares[$pattern]=$middleware;
return $this->add($pattern,$path);
}
/**中间件过滤检查:
* 1、识别出适用所有路由的中间件,
* 2、允许设置排除的几个典型路由的检查,特别是针对所有路由都适用的中间件,对login等几个路由应该可以排除
* 3、针对当前路由,识别有哪些中间件适用;
* 4、如果没有通过中间件的检查,则根据中间件提供的redirectUrl进行路由跳转
* @param Request $request
* @param Response $response
* @return bool
*/
public function executeMiddleWareChecking(Request $request, Response $response, Dispatcher $dispatcher)
{
$route = $this->getMatchedRoute();
if(null == $route) die('url地址无效,找不到对应的路由设置!');
$pattern = $route->getPattern();
//对每个路由都进行验证的中间件!
foreach($this->middlewaresForEveryRoute as $validator){
$data = null;
if(preg_match('|.*:.*|',$validator)) {//此处设置了可以带中间件参数
list($validator,$data) = explode(':',$validator);
$data = $dispatcher->getParam($data);
}
/** @var myValidation $validator */
$validator = new $validator;
if(!in_array($route->getName(),$validator->excludedRoutes) and !$validator->isValid($data)){
$url = $validator->getRedirectedUrl();
// dd($url);
$response->redirect($url,true);
return false;
}
}
if($this->hasMatchedMiddleWares($pattern)){
$middleWares = $this->getMiddleWares($pattern);
foreach($middleWares as $validator){
if($request->isPost()) $data = $request->getPost();
// dd($validator);
if(preg_match('|[^:]+:[^:]+|',$validator)){
list($validator,$data) = explode(':',$validator);
$data = $dispatcher->getParam($data);
}
if(preg_match('|.*Rules$|',$validator)){
if(!$request->isPost()) continue;//避免出现get下的错误
$rules = new $validator();
$validator = (new myValidation())->take($rules);
}else{
$validator = new $validator();
}
if(!$validator->isValid($data)){
$url = $validator->getRedirectedUrl();
// dd($url);
$response->redirect($url,true);
return false;
}
}
}
return true;
}
/**将router中参数,按照controller的中Action的类型参数进行绑定
* @param Dispatcher $dispatcher
*/
public function executeModelBinding(Dispatcher $dispatcher)
{
$reflection = new ReflectionMethod($dispatcher->getControllerClass(), $dispatcher->getActiveMethod());
$actionParams = [];
foreach($reflection->getParameters() as $parameter){
$objectId = $dispatcher->getParam($parameter->name);
if(null == $objectId && $parameter->isDefaultValueAvailable()) $objectId = $parameter->getDefaultValue();
if($parameter->getClass()){
$className = $this->getProvider($parameter->getClass()->name);
if($objectId){
if(is_subclass_of($className,\Phalcon\Mvc\Model::class)){
/** @var \Phalcon\Mvc\Model $className */
$actionParams[$parameter->name] = $className::findFirst($objectId);
}else{
$actionParams[$parameter->name] = new $className($objectId);
}
}else{
$actionParams[$parameter->name] = new $className;
}
}else{
$actionParams[$parameter->name] = $objectId;
}
}
if(count($actionParams)){
$dispatcher->setParams($actionParams);
}
}
// ---------- 提供接口绑定, 从interface 到class的绑定----------
/**
* @param $key
* @param $provider
*/
public function bindProvider($key, $provider)
{
$this->serviceProvider[$key]=$provider;
}
/**
* @param $key
* @return mixed
*/
public function getProvider($key)
{
if(isset($this->serviceProvider[$key])) return $this->serviceProvider[$key];
return $key;
}
//--------------helper functions for Middleware-----------------------------------------
/**判断是否存在对应的中间件
* @param $pattern
* @return bool
*/
private function hasMatchedMiddleWares($pattern)
{
return isset($this->middlewares[$pattern]);
}
/**获得指定的中间件字符串
* @param $pattern
* @return array
*
*
*/
private function getMiddleWares($pattern)
{
return $this->middlewares[$pattern];
}
}
And my dispatcher.php file:
use Phalcon\Events\Event;
use Phalcon\Mvc\Dispatcher;
$eventsManager = new \Phalcon\Events\Manager();
$eventsManager->attach("dispatch:beforeDispatchLoop", function(Event $event, Dispatcher $dispatcher){
//模型注入的功能,这里可以很方便的进行 model binding,这里基本上实现了Laravel中的模型绑定的功能了
return RouterFacade::executeModelBinding($dispatcher);
// return false;
});
$eventsManager->attach('dispatch:beforeExecuteRoute',function(Event $event,Dispatcher $dispatcher){
return RouterFacade::executeMiddleWareChecking(RequestFacade::getService(),ResponseFacade::getService(),$dispatcher);
});
$dispatcher = new Dispatcher();
$dispatcher->setEventsManager($eventsManager);
return $dispatcher;
I use Facade pattern mentioned in post:https://forum.phalcon.io/discussion/10371/borrow-facade-design-pattern-from-laravel .
There is already implemented model binding in phalcon 2.1.x branch in zephir:
Oh, that is good, any doc?
There is already implemented model binding in phalcon 2.1.x branch in zephir:
Just check this repo, docs will be available on phalcon page when 2.1.x it will be finally realased. There is PR waiting to be merged to docs - https://github.com/phalcon/docs/pull/785/files