2015-05-27 61 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」