2015-05-27 62 views
10

我建立使用Laravel 5.Laravel 5.0应用程序结构

试图到HTTP控制器保持到被尽可能最小的一个RESTful API,所以我使用一个服务层(和存储库)来处理最的逻辑。由于大多数控制器都有类似的方法(例如showindex,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\UpdateControllerThings\ShowController - 至少这种方式不必要的依赖不会每次都被实例化。

或者试图抽象出使用特质是做错事情的错误方法。性状有时看起来像是可能是反模式。

任何意见,将不胜感激。

+1

感谢您的详细问题。如何使用UpdateTrait中的代码创建'UpdateService'并在你的RESTful更新方法中提示它,比如'public function update($ itemID,UpdateService)'我没有测试过这个面团,但我认为它可能不会。您可能在测试中遇到问题,如果您继续使用目前的功能 – Digitlimit

+2

在L5中,您可以执行称为方法注入的操作,请参阅[此处](https://mattstauffer.co/blog/laravel-5.0-method-injection)更重要的是,我会创建授权中间件+“存在”中间件,并尽可能使用请求类。特征是很好的抽象代码,但后来你会遇到问题,我认为只是因为到目前为止,应用程序“不复杂”,时间流逝,应用程序变得越来越复杂,所以你将需要“独特”更新,修补程序也许驱逐舰(可以说你做了一些M:N的关系)。怎么样创建RestfullController?并做一些延伸? – Kyslik

+0

- 当您使用上下文绑定时,方法注入不起作用,或者至少我无法获得它。 - 我不想创建一个单独的RestfulController并进行扩展,因为其中很多不会使用大多数方法(例如,某些只有'index'和'show')。 - 我已经在使用auth中间件,但仍然有意义在服务中使用它,我认为(例如,如果使用服务而不是HTTP控制台) –

回答

1

你的代码看起来不错,思考一般化。

我想建议使用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'; 
} 

什么是这一切的想法是:

  • 保存所有的方法基本ApiController:更新,插入,删除...
  • 相互控制器特定对象将扩大api控制器并用模型全名覆盖$模型。
  • 在控制器的操作中,它使用工厂创建该特定对象的服务。
  • 创建服务后,它将其用于所需的操作。
  • ,你可以创建一个动作一般的动作,像

    DeleteServiceInterface 
    

class GeneralDeleteService implements DeleteServiceInterface { 
      public function delete($id){......} 
    } 

,并在出厂的时候,你如果没有名字被传递请求该DeleteService函数来实现所以你会返回默认服务

希望这篇文章不会是“TLDR”