Silex变量挂载点?

时间:2013-09-24 17:04:55

标签: php silex

我有一系列目前看起来像路由定义的页面:

$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作为所有四个函数的参数(indexadd,{{1 }和edit)。我想知道是否可以清除它,并可能将updateregion值移动到全局instance对象中。

我希望能够做到

$app

$app->mount('/{region}/{instance}/', new MyApp\EventsControllerProvider()); 验证EventsControllerProviderregion变量,并将“event”路由映射到该路径之上,但似乎挂载点不能包含变量(instance方法不允许额外的参数。)

有干净的方法吗?我想到了这种方法,这似乎与其他路由机制相反:

ControllerProviderInterface::connect()

3 个答案:

答案 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对象上公开。从技术上讲,任何控制器仍然可以将regioninstance参数传递给他们的方法,但现在没有必要。

答案 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());