初學(xué)者學(xué)習(xí)Laravel時分兩種,一種是乖乖的將程序填入MVC構(gòu)架內(nèi),導(dǎo)致controller與model異常的肥大,日后一樣很難維護;一種是常常不知道程序該寫在哪一個class內(nèi)而猶豫不決,畢竟傳統(tǒng)PHP都是一個頁面一個檔案。本文整理出最適合Laravel的中大型項目構(gòu)架,兼具容易維護、容易擴充與容易重復(fù)使用的特點,并且容易測試。 Controller過于肥大受RoR的影響,初學(xué)者常認為MVC構(gòu)架就是model,view,controller: 假如依照這個定義,以下這些需求該寫在哪里呢? 發(fā)送Email,使用外部API。 使用PHP寫的邏輯。 依需求將顯示格式作轉(zhuǎn)換。 依需求是否顯示某些數(shù)據(jù)。 依需求顯示不同數(shù)據(jù)。
其中1,2屬于商業(yè)邏輯,而3,4,5屬于顯示邏輯,若依照一般人對MVC的定義,model是數(shù)據(jù)庫,而view又是HTML,以上這些需求都不能寫在model與view,只能勉強寫在controller。 因此初學(xué)者開始將大量程序?qū)懺赾ontroller,造成controller的肥大難以維護。 Model過于肥大既然邏輯寫在controller不方便維護,那我將邏輯都寫在model就好了?
當(dāng)你將邏輯從controller搬到model后,雖然controller變瘦了,但卻肥了model,model從原本代表數(shù)據(jù)庫,現(xiàn)在變成還要負擔(dān)商業(yè)邏輯與顯示邏輯,結(jié)果更慘。 Model代表數(shù)據(jù)庫嗎?把它想成是Eloquent class就好,數(shù)據(jù)庫邏輯應(yīng)該寫在repository里,這也是為什么Laravel 5已經(jīng)沒有models目錄,Eloquent class僅僅是放在app根目錄下而已。 中大型項目構(gòu)架那我們該怎么寫呢?別將我們的思維局限在MVC內(nèi): Model:僅當(dāng)成Eloquent class。 Repository:輔助model,處理數(shù)據(jù)庫邏輯,然后注入到service。 Service:輔助controller,處理商業(yè)邏輯,然后注入到controller。 Controller:接收HTTP request,調(diào)用其他service。 Presenter:處理顯示邏輯,然后注入到view。 View:使用blade將數(shù)據(jù)binding到HTML。
 其中藍色為原本的MVC,而紫色為本文要介紹的的重點:Repository模式,Service模式與Presenter模式。 箭頭表示物件依賴注入的方向。 我們可以發(fā)現(xiàn)MVC構(gòu)架還在,由于SOLID的單一職責(zé)原則與依賴反轉(zhuǎn)原則: 我們將數(shù)據(jù)庫邏輯從model分離出來,由repository輔助model,將model依賴注入進repository。 我們將商業(yè)邏輯從controller分離出來,由service輔助controller,將service依賴注入進controller。 我們將顯示邏輯從view分離出來,由presenter輔助view,將presenter依賴注入進view。 建立目錄在 app 目錄建立 Repositories,Services 與 Presenters 目錄。
 別害怕在Laravel預(yù)設(shè)目錄以外建立的其他目錄,根據(jù)SOLID的單一職責(zé)原則,class功能越多,責(zé)任也越多,因此越違反單一職責(zé)原則,所以你應(yīng)該將你的程序分割成更小的部分,每個部分都有它專屬的功能,而不是一個class功能包山包海,也就是所謂的萬能類別,所以整個項目不應(yīng)該只有MVC三個部分,放手根據(jù)你的需求建立適當(dāng)?shù)哪夸洠⑦m當(dāng)?shù)腸lass放到該目錄下,只要我們的class有namespace幫我們分類即可。 Repository 由于篇幅的關(guān)系,將repository獨立成專文討論,請參考如何使用Repository模式? Service 由于篇幅的關(guān)系,將service獨立成專文討論,請參考如何使用Service模式? Presenter 由于篇幅的關(guān)系,將presenter獨立成專文討論,請參考如何使用Presenter模式? 單元測試 由于現(xiàn)在model、view、controller的相依物件都已經(jīng)拆開,也都使用依賴注入,因此每個部分都可以單獨的做單元測試,如要測試service,就將repository加以mock,也可以將其他service加以mock。 Presenter也可以單獨跑單元測試,將其他service加以mock,不一定要跑驗收測試才能測顯示邏輯。 Conclusion本文談到的構(gòu)架只是開始,你可以依照實際需求增加更多的目錄與class,當(dāng)你發(fā)現(xiàn)你的MVC違反SOLID原則時,就大膽的將class從MVC拆開重構(gòu),然后依照以下手法:
建立新的class或interface。 將相依物件依賴注入到class。 在class內(nèi)處理他的職責(zé)。 將class或interface注入到controller或view。 最后搭配單元測試,測試重構(gòu)后的構(gòu)架是否與原來的需求結(jié)果相同。
|