今天在为Silex应用程序创建一个简单的控制器时,我注意到许多动作方法几乎完全相同的代码:
public function someAction()
{
$id = $this->request->attributes->get('id')
if($id)
{
$data = getMyDataFromDatabaseWithThisID($id);
//do something with data
$this->app->json($data, 200);
}
else
{
//do error stuff
}
}
public function someOtherAction()
{
$id = $this->request->attributes->get('id')
if($id)
{
$data = getMyDataFromDatabaseWithThisID($id);
//do something else with data
$this->app->json($data, 200);
}
else
{
//do error stuff
}
}
每种方法的骨骼几乎完全相同。我想如果我有3或4个方法遵循相同的模式,我应该抽象出一些代码。
我的第一直觉是有一个很大的 executeAction($ action)方法,但我不知道从Silex路由传递变量到被调用控制器动作的方法。其次,我认为用一个巨大的方法替换许多小方法就是在寻找麻烦。
我的下一个想法是创建一个 executeAction(Closure $ action)方法,该方法由传递带有所需任务的闭包的现有操作方法调用。
因此,诸如updateAction()之类的操作方法可以将闭包传递给 executeAction(),告诉它更新所需的资源:
public function updateAction
{
$this->executeAction(function($resource, $request) {
$resource->doSomethingToUpdateItFromInfoInRequest($request->get('data'));
});
}
public function executeAction(Closure $action)
{
$id = $this->request->attributes->get('id')
if($id)
{
$data = getMyDataFromDatabaseWithThisID($id);
//execute closure action
$action($data, $this->request);
$this->app->json($data, 200);
}
else
{
//do error stuff
}
}
从表面上看,这看起来很干净。它也是灵活的,因为无论是否使用 executeAction ,都取决于每个单独的方法。我对这个想法的问题是我需要确保闭包中完成的任何事情都在闭包之外正确反映(即正确更新变量/对象等)。它也不是正确的做法。虽然这可能是由于PHP中的闭包对我来说相当新(在使用Silex之前我从未在我的PHP代码中使用过它们。)
第三种选择是将一些常见的部分减少到自己的方法 - 例如
public function getResourceById($id)
{
//do stuff
return $resource;
}
public function errorStuff($code, $message)
{
//do stuff
}
//then in action method
public function updateAction()
{
$data = $this->getResourceById($this->request->get('id');
if($data)
{
//do stuff
}
else
{
$this->errorStuff(1001, 'uh oh!');
}
}
该方法看起来更小,但存在同样的问题 - 我将有几个看起来几乎完全相同的方法,如果有变化(方法名称等),则需要更新。
所以,鉴于我正在使用Silex和Silex Controller类,这些方法中哪一个(如果有的话)是避免代码重复的更好选择?任何人都可以完全建议不同的解决方案吗?
答案 0 :(得分:1)
上面的代码对我来说看起来不像是一个silex代码库,更像是symfony控制器 所以我不会评论这个特别的。
我建议您熟悉闭包,因为它们已在Silex中大量使用。
出于您的目的,您可能需要查看Param Converters的Silex实现 他们可以参与 getMyDataFromDatabaseWithThisID 功能。此外,他们还可以处理错误。
您可以在应用中注册findOr404方法:
$app['findOr404'] = $app->protect(function($id, $message = null) use ($app) {
//get the data or abort with 404
}
并在Silex控制器中将其用作ParamConverter:
$app->get("/get/{id}", function (Data $data) {
// ...
})->convert("id", $app['findOr404']($id);
此示例的灵感来自于此Blog Post,您可以在此处找到更多技巧。
如果你在关闭时迷失了,还要看看如何包装你的controller in classes。这可能会有点熟悉。