Laravel 5.0应用程序结构

Sma*_*der 10 architecture interface laravel laravel-5

我正在使用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);
    }
}
Run Code Online (Sandbox Code Playgroud)

因为所有控制器共享相同的特征,所以它们都可以依赖于单个接口.

例如:

<?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);
}
Run Code Online (Sandbox Code Playgroud)

但是,这会导致自动依赖注入的一些问题.

1)我必须使用上下文感知绑定:

$this->app->when("API\Http\Controllers\ThingController")
          ->needs("API\Services\Interfaces\UpdateServiceInterface")
          ->give("API\Services\Things\ThingUpdateServiceInterface")
Run Code Online (Sandbox Code Playgroud)

这本身并不成问题 - 虽然它确实会导致一些相当大的服务提供商代码,但这并不理想.但是,这意味着我似乎无法使用方法注入,因为在使用上下文感知绑定时,自动依赖项解析似乎不适用于控制器方法:我只是回复一条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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这不好 - 很难测试,所有这些依赖项必须实例化,即使只使用其中一个.

但我想不出一个更好的方法.

我可以使用更具体的接口,例如ThingUpdateServiceInterface,(然后我不需要上下文绑定并且可以直接注入到特征中),但是我会有很多只在名称上不同的接口.这看起来很愚蠢.

我想到的另一个选择是使用大量较小的控制器,所以a Things\UpdateController和a Things\ShowController- 至少这种方式不必要的依赖关系不会每次都被实例化.

或者也许试图抽象使用特征是错误的做事方式.特征有时看起来像是一种反模式.

任何意见,将不胜感激.

Tzo*_*Noy 2

您的代码看起来很棒并且经过深思熟虑的概括。

我想建议使用此选项来使用工厂来创建服务,而不是预定义所有依赖项注入。

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';
}
Run Code Online (Sandbox Code Playgroud)

这一切的想法是什么:

  • 基本的 ApiController 包含所有方法:更新、插入、删除......
  • 特定对象的每个其他控制器将扩展 api 控制器并使用模型全名覆盖 $model。
  • 在控制器的操作中,它使用工厂为该特定对象创建服务。
  • 创建服务后,它将使用它来执行所需的操作。
  • 您可以为该操作创建一般操作,例如

    DeleteServiceInterface
    
    Run Code Online (Sandbox Code Playgroud)

将由

    class GeneralDeleteService implements DeleteServiceInterface {
           public function delete($id){......}
    }
Run Code Online (Sandbox Code Playgroud)

在工厂中,当您请求该删除服务时,如果没有传递名称,那么您将返回默认服务

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