最近用laravel做自己的個人博客,過程中也思考了一些問題,如何把自己的代碼寫的更優(yōu)雅呢,為什么laravel沒有models目錄呢,邏輯代碼,數(shù)據(jù)庫查詢代碼要怎樣放置呢?
我們一直以來都被灌輸?shù)脑O(shè)計思想,即M-V-C,模型(Model)、視圖(view)、控制器(Controller)某種程度上是因為Ruby on Rails的流行。Model在大部分開發(fā)者看來就是數(shù)據(jù)庫操作之類的東西,但是在實際項目開發(fā)的過程中,我們會有大量的邏輯代碼,如數(shù)據(jù)驗證,調(diào)用外部服務(wù),發(fā)送電子郵件等,所以很多開發(fā)者就將業(yè)務(wù)邏輯封裝到控制器里,當控制器龐大到一定規(guī)模,它們將會需要復(fù)用其他控制器中的業(yè)務(wù)邏輯。大部分開發(fā)人員并沒有將這些業(yè)務(wù)邏輯提取到另外的類里面,而是錯誤的以為需要在控制器里面調(diào)用其他的控制器方法。這種模式通常被稱為「HMVC」。遺憾的是,這種模式通常也意味著糟糕的程序設(shè)計,以及控制器太過復(fù)雜。
HMVC 意味著糟糕的設(shè)計:你覺得需要在控制器里面調(diào)用其他的控制器?這通常意味著糟糕的程序設(shè)計,以及你的控制器里面包含了過多的業(yè)務(wù)邏輯。好的做法是把控制器中的業(yè)務(wù)邏輯提取出來,放到一個新的第三方類里面,通常,我們將這種第三方類稱之為服務(wù)類,這樣你就可以在所有其他控制器里面注入服務(wù)類并使用它們了。
有一種更好的方式來構(gòu)建應(yīng)用程序結(jié)構(gòu)。但首先我們要忘掉以往我們被灌輸?shù)年P(guān)于「模型」的一切。干脆點,讓我們直接刪掉模型目錄,重新開始吧!
再見,模型
刪掉你的 models 目錄了嗎?還沒刪就趕緊刪了!我們將要在 app 目錄下創(chuàng)建一個新的目錄,目錄名就以我們這個應(yīng)用的名字來命名,比如 QuickBill。我們將繼續(xù)使用在前面章節(jié)中編寫的那些接口和類。
>注意使用場景:記住,如果你在構(gòu)建一個很小的 Laravel 應(yīng)用,那在models 目錄下寫幾個 Eloquent 模型其實挺合適的。但在本章節(jié),我們主要關(guān)注如何開發(fā)適用于分層架構(gòu)的大型復(fù)雜項目。
所以,我們現(xiàn)在有了個 app/QuickBill 目錄,它和應(yīng)用目錄下的其他目錄如 Http 還有 Console 都是平級的。在 QuickBill 目錄下我們還可以創(chuàng)建幾個其他目錄,例如 Repositories 和 Billing 目錄。目錄都創(chuàng)建好以后,別忘了在 composer.json 文件里通過 PSR-4 自動載入機制將它們注冊到 QuickBill 命名空間下:
現(xiàn)在,我們把所有 Eloquent 模型類都放到 QuickBill 目錄下面。這樣我們就能很方便的以 QuickBill/User、QuickBill/Payment 這種方式來使用它們。Repositories 目錄下包含 PaymentRepository 和UserRepository 這種倉庫類,倉庫類里面包含了所有對數(shù)據(jù)的訪問功能,比如 getRecentPayments 和 getRichestUser。Billing 目錄包含了調(diào)用第三方支付服務(wù)(如 Stripe 和 Balanced)的類和接口。整個目錄結(jié)構(gòu)現(xiàn)在應(yīng)該類似這樣:
數(shù)據(jù)驗證放在哪?在哪兒進行數(shù)據(jù)驗證常常困擾著開發(fā)人員??梢钥紤]將數(shù)據(jù)驗證方法寫進你的「實體」類里面(例如 User.php 和 Payment.php)。方法名可以設(shè)置為 validForCreation 或 hasValidDomain?;蛘吣阋部梢詫iT創(chuàng)建一個驗證器類 UserValidator,放到 Validation 命名空間下,然后將這個驗證器類注入到你的 Repository 類里面。兩種方式你都可以試試,看哪個你更喜歡!當然在 Laravel 5.* 中,你不需要自己創(chuàng)建驗證器類了,通過 Laravel 自帶的驗證器類就可以滿足你的所有需求。
擺脫了 models 目錄的束縛后,你通常就能克服實現(xiàn)好的架構(gòu)設(shè)計的心理障礙,也就能夠構(gòu)建一個更合適應(yīng)用的目錄結(jié)構(gòu)。當然,你構(gòu)建的每一個應(yīng)用程序都會有一定的相似之處,因為不管多復(fù)雜的應(yīng)用程序都需要一個數(shù)據(jù)訪問層(Repository),以及一些外部服務(wù)層等等。
別害怕目錄:不要懼怕創(chuàng)建更多目錄來組織管理應(yīng)用。將整個應(yīng)用切割成多個細分的功能組件總是必要的,每一個功能組件都專注于某一項職責。跳出「模型」的框框來思考總是有幫助的。例如,我們之前就討論過,你可以創(chuàng)建一個 Repositories 目錄來存放所有的數(shù)據(jù)訪問類。
核心思想就是分層
你可能已經(jīng)注意到,優(yōu)化應(yīng)用目錄結(jié)構(gòu)的關(guān)鍵就是對不同組件的責任進行劃分,或者說為不同的職責創(chuàng)建不同的層??刂破髦回撠熃邮蘸晚憫?yīng) HTTP 請求,然后調(diào)用合適的業(yè)務(wù)邏輯層的類。你的業(yè)務(wù)邏輯/領(lǐng)域邏輯層才是應(yīng)用最核心的部分,其中包含了讀取數(shù)據(jù),驗證數(shù)據(jù),執(zhí)行支付,發(fā)送電子郵件,還有程序里所有其他功能的代碼。事實上,你的領(lǐng)域邏輯層不需要知道任何關(guān)于「Web」的事情!Web 層僅僅是一種訪問應(yīng)用程序的傳輸機制,關(guān)于 Web 和 HTTP 請求的一切不應(yīng)該超出路由和控制器層的范圍。做出好的架構(gòu)設(shè)計的確很有挑戰(zhàn)性,但好的架構(gòu)設(shè)計也會帶來可維護的、更加清晰的代碼。
舉個例子,與其在業(yè)務(wù)邏輯類里面直接獲取 Web 請求,不如把 Web 請求通過控制器傳遞給業(yè)務(wù)邏輯類。這個簡單的改動會將你的業(yè)務(wù)邏輯類和「Web」層解耦,并且不必擔心怎么去模擬 Web 請求,就可以輕松測試業(yè)務(wù)邏輯類:
現(xiàn)在 chargeAccount 方法更容易測試了,由于我們不再需要在 BillerInterface 實現(xiàn)類中使用 User 和 Request 類,只需將用戶和金額傳遞到該方法即可。
編寫可維護性應(yīng)用程序的關(guān)鍵之一,就是職責分離。要時常檢查一個類是否管得太寬,知道一些它不該知道的。你要常常問自己「這個類是否需要關(guān)心X?」如果答案是否定的,那么就要把這塊邏輯提取出來放到另一個類里面,然后用依賴注入的方式將其注入進來。
如何判斷一個類是否管得太寬?一個有用的方法就是檢查你為什么要改這塊代碼。舉個例子,當我們想調(diào)整通知邏輯的時候,是否需要修改 Biller 的實現(xiàn)代碼?當然不需要,Biller 實現(xiàn)只關(guān)注支付,它與通知邏輯應(yīng)當僅通過契約來進行交互。在處理代碼時使用這種思路,可以幫助你快速找出應(yīng)用中需要改進的地方。
東西都放哪兒?
當通過 Laravel 開發(fā)應(yīng)用時,你可能會困惑于應(yīng)該把各種「東西」放在哪里。例如,輔助函數(shù)要放在哪里?事件監(jiān)聽器要放在哪里?視圖組件要放在哪里?答案可能出乎你的意料 —— 「隨便,放哪兒都行!」Laravel 并沒有很多關(guān)于這方面的文件系統(tǒng)上的約定。不過,這個答案的確不能讓人滿意,所以下面我們就這個問題展開討論,一起探討這些「東西」究竟可以放在哪里。
輔助函數(shù)
我們可以在 app 目錄下創(chuàng)建一個 helpers.php 文件,然后將自定義的輔助函數(shù)都放到這個文件中。當然,由于這個文件里面包含的不是類,不適合通過命名空間引用其中的函數(shù),需要在 composer.json 中全局注冊它:
然后運行 composer dump-auto 重新注冊自動加載映射關(guān)系。然后就可以在應(yīng)用中使用 app/helpers.php 中定義的輔助函數(shù)了。
事件監(jiān)聽器
事件監(jiān)聽器當然不該放到 routes.php 文件里面,所以我們要找另外的地方來存放。事實上,在 Laravel 5.* 中,當我們通過 php artisan make:listener 命令創(chuàng)建時間監(jiān)聽器時,系統(tǒng)會自動在 app 目錄下創(chuàng)建 Listeners 子目錄,并將生成的監(jiān)聽器類放到該目錄下。所以我們對于自定義的事件監(jiān)聽器類,放到該目錄下即可。
錯誤處理器
在 Laravel 5.* 版本中,默認情況下所有的異常都是通過 App/Exceptions/Handler 來處理的,你也可以通過自定義的異常類來處理,自定義異常類可以放到 app/Exceptions 目錄下。
其他
通常,只要遵循 PSR-4 規(guī)范就可以在應(yīng)用目錄結(jié)構(gòu)中保持類的整齊。結(jié)合你目前為止學習到的知識,對于什么代碼要放在什么地方這個問題,應(yīng)當可以給出一個有理有據(jù)的答案了。但永遠不要拒絕試驗。Laravel 的美妙之處就是你可以做出最適合你自己應(yīng)用的約定。去探索和發(fā)現(xiàn)最適合你自己應(yīng)用的結(jié)構(gòu)吧,別忘了和他人分享你的見解!
例如,你可能受到我們之前例子的啟發(fā),在 QuickBill 中創(chuàng)建一個 Providers 目錄來存放所有你自定義的服務(wù)提供者,目錄結(jié)構(gòu)類似這樣:
注意上面的例子,我們有 Providers 和 Extensions 兩個命名空間。所有你自定義的服務(wù)提供者都可以放到 Providers 命名空間下,而 Extensions 命名空間可以用來存放你對框架核心進行擴展的類。
【責任編輯:武曉燕 TEL:(010)68476606】