二、 MVC 架構實戰與本專案程式碼對應 - amattsuyolo/cham GitHub Wiki

參考文章:

主題專注於前一章提到的下列三個部分做說明

  1. Repository : 輔助 model,處理資料庫邏輯,然後注入到service (或controller)。
  2. Service : 輔助 controller,處理商業邏輯,然後注入到 controller。
  3. Controller : 接收 HTTP request,調用其他 service。

並搭配本專案程式碼 路徑為cham/app/Http/Controllers/AuthController.php

主要以 sendValidateSms (寄驗證簡訊) 方法做說明

一、建立相對應的資料夾 Repositories、Services、Entity

二、處理Entity(model)

使用 Repository 模式之後,Model 僅僅當成 Eloquent Class 即可,不需要包含數據庫邏輯,僅保留如下部分:

Property: 如 $table $fillable ..

Mutator: 包括 mutator 與 accessor

Method: relation 類的方法,比如使用 hasMany() 與 belongsTo()

本專案將User.php 置於 Entity外,是由於使用passport 建置api的token機制,下圖是User的內容( 可參照程式碼),僅僅當成了Eloquent Class , 除了User以外的表將放置於Entity folder

三、開始著手看方法:

 /*
    寄驗證信 
    若成功寄出 更新user表驗證碼欄位
 *  @param  json $user_id , $phone_number
 *
 *  @return [json]
 *  
*/ 
public function sendValidateSms(Request $request){
    $request->validate([
        'phone_number' => 'required',
        'user_id' => 'required'
    ]);
    // 使用者id
    $user_id=$request->user_id;
    // 手機號碼
    $phone=$request->phone_number;
    // 亂碼表
    $garbled_array=[0,1,2,3,4,5,6,7,8,9];
    shuffle($garbled_array);
    // 亂碼產生
    $verify_code=$garbled_array[0].$garbled_array[1].$garbled_array[2].$garbled_array[3];        
    // 簡訊內容
    $content="VerifyCode:"."\r\n".$verify_code;
    // 寄出簡訊並抓取回傳值
    $result=$this->smsService->sendSms($phone,$content);
    if($result==0){
        // 若成功,更新資料庫user verify欄位
        $this->userRepository
            ->verificationCodeUpdate($user_id,$verify_code);
        return response()->json(
            [
            "message" => "Successfully send demo sms"
            ], 200
        );
    }else{
    // 錯誤重寄....
    }
}

可以清楚看出該方法須傳入使用者id與電話號碼

成功驗證傳入值後,產生驗證碼並利用第三方服務(Nexmo)寄簡訊

若簡訊成功寄出,則將產生驗證碼存入資料庫

並回傳成功訊息

四、講述Services (輔助 controller,處理商業邏輯,然後注入到 controller。)

為了能讓寄簡訊功能能輕易的給未來不同的class重複使用,故在Services folder建立 SmsService.php

若有需要串接簡訊服務可參考下列文章

https://github.com/Nexmo/nexmo-laravel

https://blog.scottchayaa.com/post/2018/10/30/laravel-nexmo-sms/

將檔案建構完成後我們將其利用建構子注入於authcontroller中

優點:

將外部行為寫在 Service,解決了 Controller 代碼臃腫的問題。

符合 SOLID 的單一職責原則: 外部行為寫在 Service ,沒寫在 Controller。

符合 SOLID 的依賴反轉原則:Controller 並非直接相依於 Service,而是將 Service 依賴注入進 Controller。

未來情境推演:

發現有間簡訊商B寄簡訊較便宜,但我們的金流、會員登入註冊流程、廣告的CLASS都有使用寄簡訊功能

此時我們僅僅需要SERVICE中的SmsService 調整為能用簡訊商B寄信即可

五、講述Repository (輔助 model,處理資料庫邏輯,然後注入到 controller或service。)

為了要處理寄信成功後,更新資料表驗證碼的部分,我們在Repositories中新增了UserRepository.php

可以看出我們將第二點的 Model 依賴注入到 Repository。

上圖是更新驗證碼的內容

優點:

將數據庫邏輯寫在 Repository 裡,解決了 Controller 代碼臃腫的問題。

符合 SOLID 的單一職責原則:數據庫邏輯寫在 Repository 裡,沒寫在 Controller 裡。

符合 SOLID 的依賴反轉原則:Controller 並非直接相依與 Repositroy,而是將 Repository 依賴注入進 Controller。

實際上建議 Repository 僅依賴注入進 Service,而不是直接注入在 Controller。

是否該建立 Repository Interface? 理論上使用依賴注入時,應該使用Interface ,不過Interface 目的在於更換數據庫,讓代碼達到開放封閉的要求,但是實際上要更改Reposiroty 的機會也不多,除非是從MySQL 更換到MongoDB,此時就應該建立Repository Interface。

不過由於我們使用了依賴注入,將來要從Class 改成Interface 也很方便,只要在Constructor 的type hint 改成Interface 即可,維護成本很低,所以在此大可使用Repository Class 即可,不一定得用Interface而造成Over Design,等真正需要修改時,再重構Interface 即可。