Laravel 5.0应用程序结构

时间:2015-05-27 08:54:41

标签: laravel interface architecture laravel-5

我正在使用Laravel 5构建RESTful API。

尽量让Http控制器保持尽可能小,所以我使用服务层(和存储库)来处理大部分逻辑。

由于大多数控制器都有类似的方法(例如showindexupdate),我写了一些处理每个控制器的特征。因为这些直接与服务对话,所以我可以为每个控制器重复使用它们。

例如:

<?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; } } ,(然后我不需要上下文绑定并且可以直接注入特征),但是我会有很多只在名称上不同的接口。这看起来很愚蠢。

我想到的另一个选择是使用许多较小的控制器,所以ThingUpdateServiceInterfaceThings\UpdateController - 至少这样不必要的依赖关系每次都不会被实例化。

或者尝试抽象使用特征是错误的做事方式。特征有时看起来有点像反模式。

任何建议都将受到赞赏。

1 个答案:

答案 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';
}

所有这一切的想法是:

  • 基本的ApiController,包含所有方法:update,insert,delete ...
  • 特定对象的每个其他控制器将扩展api控制器并使用Model全名覆盖$ model。
  • 在控制器的操作中,它使用工厂为该特定对象创建服务。
  • 在创建服务后,它将其用于所需的操作。
  • 您可以为操作创建一般操作,例如

    DeleteServiceInterface
    

将由

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

并且在工厂中,如果没有传递名称,则请求删除该服务,因此您将返回默认服务

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