0

我有一系列页面,目前看起来像这样作为路由定义:

$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类,它必须具有regioninstance作为所有四个函数(、、、和index)的参数。我想知道是否可以清理它,并可能将and值移动到全局对象中。addeditupdateregioninstance$app

我很想能够做到

$app->mount('/{region}/{instance}/', new MyApp\EventsControllerProvider());

EventsControllerProvider验证regioninstance变量,并将“事件”路由映射到该路径的顶部,但似乎挂载点不能包含变量(该ControllerProviderInterface::connect()方法不允许额外的参数)。

有没有一种干净的方法可以做到这一点?想到了这个方法,好像和其他路由机制背道而驰:

$app->get('/{region}/{instance}/{action}' function($region, $instance, $action) use ($app) {

  if ($app['myapp.valid-region']($region) === false) return $app->abort(404);
  if ($app['myapp.valid-instance']($instance) === false) return $app->abort(404);


  switch ($action) {
    case 'events':
      $c = new MyApp\EventsController();
      return $c->index();
  }
});
4

3 回答 3

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从技术上讲,任何控制器仍然可以将regioninstance参数传递给他们的方法,但现在不需要这样做。

于 2016-02-07T06:13:21.460 回答
0

这不完全是您的问题,但我需要一些类似于以下形式的资源和子资源的 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} 可以直接访问。

于 2013-10-01T08:38:29.843 回答
0

如果您可以通过正则表达式检查有效区域实例,则可以在connect方法中添加断言声明:

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 s:

$app->mount('/{region}/{instance}/', new EventsControllerProvider());
于 2015-07-30T10:40:46.040 回答