我有一系列目前看起来像路由定义的页面:
$app->get('/{region}/{instance}/events', 'MyApp\EventsController::index');
$app->post('/{region}/{instance}/events', 'MyApp\EventsController::add');
$app->get('/{region}/{instance}/event/{id}', 'MyApp\EventsController::edit');
$app->post('/{region}/{instance}/event/{id}', 'MyApp\EventsController::update');
然后我有一个EventsController
类,必须有region
和instance
作为所有四个函数的参数(index
,add
,{{1 }和edit
)。我想知道是否可以清除它,并可能将update
和region
值移动到全局instance
对象中。
我希望能够做到
$app
让$app->mount('/{region}/{instance}/', new MyApp\EventsControllerProvider());
验证EventsControllerProvider
和region
变量,并将“event”路由映射到该路径之上,但似乎挂载点不能包含变量(instance
方法不允许额外的参数。)
有干净的方法吗?我想到了这种方法,这似乎与其他路由机制相反:
ControllerProviderInterface::connect()
答案 0 :(得分:2)
多年以后,我找到了解决方案。它当时可能不存在,但它现在确实存在。正如我在原始问题中提到的那样,ControllerProvider
没有其他可能有用的参数,但中间件可以解决这个问题:
$app->mount('/{region}/{instance}/', new MyApp\EventsControllerProvider())
->before(function(Request $r, Application $a) {
$region = $r->attributes->get('region');
$instance = $r->attributes->get('instance');
$regionInfo = $a['myRegionService']($region, $instance);
if ($regionInfo === false) {
return $a->abort(404, 'No such region');
}
$a['currentRegionInfo'] = $regionInfo;
});
现在,我的EventsControllerProvider
下安装的控制器不需要对区域/实例进行任何验证,因为事先已经完成(如果出现问题则返回404)。如果控制器需要知道当前区域,他们可以将其从$a['currentRegionInfo']
中拉出来,因为这已经设置好了。由于此中间件仅绑定到此特定路由集合,因此不会为应用程序中定义的其他路由调用它。
URL中定义的变量在Request
参数下的attributes
对象上公开。从技术上讲,任何控制器仍然可以将region
和instance
参数传递给他们的方法,但现在没有必要。
答案 1 :(得分:1)
这不完全是你的问题,但我需要一些类似于API的资源和子资源的形式:
/resources/{resource_id}/subresources/{subresource_id}
可以使用不同的子资源。
我结束先对资源进行匹配,然后创建一个新的子资源请求,并将resource_id作为查询参数。
我认为这不是理想而且非常干净但是它有效(现在):
$app->mount('/subresource/1', new Subresource1ControllerProvider());
// etc for subresource 2/3/n
$app->match('/resources/{resource_id}/{slug}', function (Request $request, $resource_id, $slug) use ($app)
{
$currentQueryParams = $request->getQueryString();
$newQueryParams = '?resource_id=' . $resource_id .
((empty($currentQueryParams)) ? '' : '&' . $currentQueryParams);
$subRequest = Request::create('/' . $slug . $newQueryParams, $request->getMethod(), $request->request->all(),
$request->cookies->all(), array(), $request->server->all(), $request->getContent());
if ($request->getSession()) { $subRequest->setSession($request->getSession()); }
return $app->handle($subRequest, HttpKernelInterface::SUB_REQUEST);
})
->assert('slug', '.*');
然后在我的控制器提供商中:
class Subresource1ControllerProvider implements ControllerProviderInterface
{
public function connect(Application $app)
{
$controllers = $app['controllers_factory'];
$controllers->get('/', function (Request $request) use ($app) {
$app['resource.id'] = $request->query->get('resource_id');
$resource = $app['resource'];
// do your thing ...
return /* whatever */
});
return $controllers;
}
}
缺点是它也可以直接访问/ subresource / {subresource_id}。
答案 2 :(得分:0)
如果您可以通过正则表达式检查有效的 region 和实例,则可以在 connect <中添加断言声明/ em>方法:
namespace MyApp;
class EventsControllerProvider implements ControllerProviderInterface
{
public function connect(Application $app)
{
$controller = $app['controllers_factory'];
$controller->get('/', [ $this, 'handleRoot' ]);
$controller->assert('region', 'europe|asia|america');
$controller->assert('instance', 'some regular expression');
return $controller;
}
public function handleRoot(Request $request)
{
$region = $request->attributes->get('region');
$instance = $request->attributes->get('instance');
...
return ...;
}
}
然后你有 mount 的地方:
$app->mount('/{region}/{instance}/', new EventsControllerProvider());