【发布时间】:2015-05-27 08:54:41
【问题描述】:
我正在使用 Laravel 5 构建一个 RESTful API。
尽量减少 Http 控制器,因此我使用服务层(和存储库)来处理大部分逻辑。
由于大多数控制器都有类似的方法(例如show、index、update),我编写了一些处理每一个的特征。因为这些直接与服务对话,所以我可以为每个控制器重用这些。
例如:
<?php namespace API\Http\Controllers\Restful;
trait UpdateTrait
{
protected $updater;
public function update($itemID)
{
if (!$this->updater->authorize($itemID)) {
return response(null, 401);
}
if (!$this->updater->exists($itemID)) {
return response(null, 404);
}
$data = $this->request->all();
$validator = $this->updater->validator($data);
if ($validator->fails()) {
return response($validator->messages(), 422);
}
$this->updater->update($itemID, $data);
return response(null, 204);
}
}
因为所有控制器共享相同的特征,所以它们都可以依赖于一个接口。
例如:
<?php namespace API\Services\Interfaces;
interface UpdaterServiceInterface
{
public function validator(array $data);
public function exists($itemID);
public function update($itemID, array $data);
public function authorize($itemID);
}
但是,这会导致自动依赖注入出现一些问题。
1) 我必须使用上下文感知绑定:
$this->app->when("API\Http\Controllers\ThingController")
->needs("API\Services\Interfaces\UpdateServiceInterface")
->give("API\Services\Things\ThingUpdateServiceInterface")
这本身并没有问题——尽管它确实会导致一些相当大的服务提供者代码,这并不理想。但是,这意味着我似乎无法使用方法注入,因为在使用上下文感知绑定时,自动依赖解析似乎不适用于控制器方法:我刚收到could not instantiate API\Services\Interfaces\UpdateServiceInterface 消息.
这意味着控制器构造函数必须处理所有的依赖注入,这变得相当混乱:
class ThingsController extends Controller
{
use Restful\IndexTrait,
Restful\ShowTrait,
Restful\UpdateTrait,
Restful\PatchTrait,
Restful\StoreTrait,
Restful\DestroyTrait;
public function __construct(
Interfaces\CollectionServiceInterface $collection,
Interfaces\ItemServiceInterface $item,
Interfaces\CreatorServiceInterface $creator,
Interfaces\UpdaterServiceInterface $updater,
Interfaces\PatcherServiceInterface $patcher,
Interfaces\DestroyerServiceInterface $destroyer
) {
$this->collection = $collection;
$this->item = $item;
$this->creator = $creator;
$this->updater = $updater;
$this->patcher = $patcher;
$this->destroyer = $destroyer;
}
}
这不好 - 很难测试,所有这些依赖项都必须实例化,即使只使用其中一个也是如此。
但我想不出更好的解决方法。
我可以使用更具体的界面,例如ThingUpdateServiceInterface,(这样我就不需要上下文绑定并且可以直接注入到特征中),但是我会有很多只是名称不同的接口。这似乎很愚蠢。
我想到的另一种选择是使用许多较小的控制器,例如 Things\UpdateController 和 Things\ShowController - 至少这样就不会每次都实例化不必要的依赖项。
或者也许试图抽象到使用特征是错误的做事方式。 trait 有时确实看起来像是一种潜在的反模式。
任何建议将不胜感激。
【问题讨论】:
-
感谢您提出详细的问题。如何使用 UpdateTrait 中的代码创建
UpdateService并在 RESTful 更新方法中输入提示,例如public function update($itemID, UpdateService)我还没有测试过这个面团,但我认为它可以不测试。如果您继续目前的测试,您的测试可能会遇到问题 -
在L5中你可以做所谓的方法注入参见here更多,我会创建授权中间件+“存在”中间件,并尽可能使用请求类。 Traits 是很好的代码抽象,但后来你会遇到问题,我认为仅仅是因为到目前为止应用程序“并不复杂”,随着时间的推移,应用程序变得越来越复杂,所以你将需要“独特”的更新程序、修补程序也许是驱逐舰(假设你做了一些 M:N 关系)。创建 RestfullController 怎么样?并做一些扩展?
-
- 当您使用上下文绑定时,方法注入不起作用,或者至少我无法做到。 - 我不想创建单个 RestfulController 并进行扩展,因为其中有不少不会使用大多数方法(例如,有些只有
index和show)。 - 我已经在使用 auth 中间件,但我认为在服务中使用它仍然是有意义的(例如,如果将服务用于控制台而不是 HTTP) -
似乎有几个issues关于上下文绑定和方法注入打开,但直到现在还没有解决方案。这是一个错误,我希望它会很快得到修复。您可以在 git 问题跟踪中打开新问题,希望我们能引起 Taylor 的注意。
-
我认为如果上下文绑定在方法上起作用,那么一切都会很整洁。也许这就是一切。
标签: laravel interface architecture laravel-5