我正在使用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")
这本身并不成问题 - 虽然它确实会导致一些相当大的服务提供商代码,但这并不理想。但是,这意味着我似乎无法使用方法注入,因为自动依赖关系解析在使用上下文感知绑定时似乎不适用于控制器方法:我只是回到{{1}消息。
这意味着控制器构造函数必须处理所有依赖注入,这会非常混乱:
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
- 至少这样不必要的依赖关系每次都不会被实例化。
或者尝试抽象使用特征是错误的做事方式。特征有时看起来有点像反模式。
任何建议都将受到赞赏。
答案 0 :(得分:2)
你的代码看起来很棒,并且很好地考虑了泛化。
我想建议使用Factory来创建服务,而不是预定义所有依赖注入。
interface UpdateServiceInterface {
public function action($itemID);
//....
}
class ThingUpdateServiceInterface implements UpdateServiceInterface {
public function action($itemID)
{
// TODO: Implement exists() method.
}
}
class ApiServiceFactory {
protected $app;
public function __construct(Application $app) {
$this->app = $app;
}
public function getUpdateService($type) {
if ($type == 'things')
return $this->app->make('ThingUpdateServiceInterface');
}
public function getCreateService($type) {
if ($type == 'things') {
//same idea here and for the rest
}
}
}
class ApiController {
protected $factory;
protected $model;
public function __construct(ApiServiceFactory $factory) {
$this->factory = $factory;
}
public function update($data) {
$updater = $this->factory->getUpdateService($this->model);
$updater->action($data);
}
}
class ThingsController extends ApiController {
protected $model = 'App\Thing';
}
所有这一切的想法是:
您可以为操作创建一般操作,例如
DeleteServiceInterface
将由
实施 class GeneralDeleteService implements DeleteServiceInterface {
public function delete($id){......}
}
并且在工厂中,如果没有传递名称,则请求删除该服务,因此您将返回默认服务
希望这篇文章不会是“TLDR”