學習足跡

2015年1月26日 星期一

在 Laravel 4 使用資源庫 (Repositories) 及服務 (Services) 去降低程式的耦合性

文章來源:Decoupling your code in Laravel using Repositiories and Services

當開始使用 Laravel 的時候,你可以找到有好幾個地方可以建立你自己的目錄結構,而模型(Models)則是最大的一個結構,Laravel 則是建議模型最好是使用 Eloquent 物件去設計,並告知 Laravel 這個模型指的是哪一個資料表 - 而程式的商業邏輯比較不適合放在模型這個地方。

這邊文章重點在於將不同用途的模型放在不同的地方,藉此去降低程式碼的耦合性,更重要的是特別在你的應用正在成長中且要發展較快的時候,你可以有可重複使用性的程式碼。

我們在這裡會實作資源庫 (Repository) 設計模式結合服務 (Service) 導向的架構,這個是我喜歡在乾淨的 Laravel 安裝時,分離程式邏輯的方法,可能值得注意的是,我過去開發很多 Symfony 且這樣的實做是有很大的參考基礎的。

不同類型的模型

在實作這些元素時,我們最終會留下 3 個不同類型的模型

實體 (Entities):

標準的 Laravel 模型,裡面只有包含設定的變數、被 Eloquent 使用的方法、關聯關係、訪問器及存取器。

資源庫 (Repositories):

使用實體 (Entities) 去取得你的資料,裡面的函式去收集應用程式需要的特定資料集,雖然在裡面的邏輯可能會異動,但是回傳的資料格式應該都是相同的。

服務 (Services):

全域資料邏輯的家,包含被你應用程式的使用的函式,他可以稱為是你的資源庫 (Repositories) ,也是你的資料驗證器 (validates)、建立 session 且包含你的邏輯,這些可以幫助你的控制器 (controller) 變得更為輕盈!



所以什麼是資源庫 (Repository) 的設計模式?

資源庫 (Repository) 是一個與你的資料來源做交談的通用類別(在這個情況是指你的實體 (Entity)),且回傳非常特定的輸出格式,如果資料來源被異動(例如:在API中),你可以交換你的舊資源庫變成新的資源庫,這兩個資源庫會回傳相同輸出格式的資料,意味著你應用程式的其他部分都不需要做異動!

使用這個模式只需要一點點額外的功夫就可以,但有一天你會感謝你的幸運之星,你做到了!

而服務 (Service) 導向的架構?

這個一般會被考慮做為替代 MVC 的架構,但其實不需要這麼做,Laravel 已經使用很多服務 (Service) 在他自己的 MVC 環境中,你或許知道他們叫做 Facades,藉由這個模式,你可以在網站中任一地方,就像 Facades 一樣很容易的重新使用函式及方法。

介面 (Interfaces)、服務提供者 (Service Providers) 及 Facade 類別

在我們開始之前,這裏有三種檔案類型值得去了解一下,這會讓這些設置變得像魔術一樣,這三個分別是介面 (Interfaces)、服務提供者 (Service Providers) 及 Facade 類別

介面是讓資源庫可以通用的關鍵,它包含了類別所需要的資訊,且在沒有得到介面想要的結果時會丟出資訊告知我們,這讓我們不會忘記任何類別所需要的方法,並保護我們的應用。

服務提供者 (Service Providers) 散落在 Laravel 各處,且通常會在供應商 (vendor) 那邊找到,他們告訴 Laravel 有關你的類別且要怎麼去建立他,對於依賴性注入是非常重要的。

最後在這三者之中最簡單的是 Facade 類別,每當你嘗試要使用 Facade,Laravel 會到這個類別中,並僅取得類別的名稱並回傳,就這麼簡單!

那我們開始吧...

第一步是將你的模型放到「app/models/entities」目錄中,這些模型必須要有「Entities」的命名空間,在建立資料夾同時,我們也需要「app/models/repositories」及「app/models/services」這兩個資料夾。

現在,我們可以說我們有一個資料表包含所有 151 個神奇寶貝,你當然會有一個命名空間為「\Entities\Pokemon」這個類別的實體,在你的資源庫資料夾中,建立一個新的「pokemon」資料夾。



在我們建立類別之前,我們首先必須建立一個介面 (interface),這樣有一個好處是可以讓我們好好想想並計劃我們的資源庫 (Repository) 內需要包含哪些東西。


每當我們在類別中實作這個介面 (interface),我們可以說這個類別必須要包含這兩種方法,每一個接收變數都會被描述。

接下來,讓我們建立我們的類別。



三件重要的事情會發生在這裡,首先,我們實作我們剛剛做的介面 (interface),再來我們注入我們的 Pokemon 實體 (Entity)到資源庫中 (Repository),最後在回傳相關的 eloquent 物件時,我們將它轉換為一般的物件,如此一來當資料來源有異動時,我們還是可以回傳相同格式的物件實體。

在我們資源庫準備完成的最後階段,則是告訴 Laravel 他的相關資訊!

使用註冊 (register) 及綁定 (bind) 函式告訴 Laravel 去使用這個類別。



我們現在已經綁定我們的 PokemonRepository 資源庫到 PokemonInterface 介面中,一旦我們把這個服務提供者 (Service Provider) 加入到你的「app/config/app.php」當中(稍後會提到更多相關介紹),我們可以注入我們的資源庫到任何我們喜歡的地方,而 Laravel 將會注入這個類別。

假如我們決定去建立一個新的 PokemonRepository 資源庫,我們可以在這裡簡單地告訴 Laravel ,且在我們應用中所有的注入參照將會使用這個參照。

讓我們建立服務 (Service)!

就像資源庫 (Repository) 一樣,有三個階段去實作它,在我們不需要去替換類別時,我們不用去擔心有關介面的部分,但我們需要其他的服務提供者 (Service Provider),Facade 類別及服務 (Service) 本身。

因為我們只有建置了一個我們的資源庫 (Repository),讓我們開始來建立服務提供者 (Service Provider),就像你的資源庫 (Repository) 一樣,我們需要在「model/services」裡建立一個新的「pokemon」資料夾給這個服務 (Service)。



所以就像先前一樣我們會告訴 Laravel 有關這個服務放置的位置並建立類別,然後...等等,那是什麼?

朋友啊,那個就是 Laravel 聰明的地方!Laravel 可以看到我們需要一個實作 PokemonInterface 介面的類別,所以 Laravel 就注入這個介面類別。

現在我們可以建立我們的服務 (Service),它應該看起來會像這樣:



一個非常簡單的類別去顯示在你的服務 (Service) 裡去使用資源庫 (Repository),當然這個方法可以變得更加複雜,較少強調在資源庫上,最後一個檔案去建立 Facade 類別,這是非常簡單且不需要額外解釋的類別:



服務提供者 (Service Provider)  - 檢查 OK

服務 (Service) 類別  - 檢查 OK

Facade 類別  - 檢查 OK

在我們完成這個之前,我們必須要能自動載入我們剛剛新增的所有類別,在你的 composer.json 檔案中我們必須要在你的 class map 註冊新的資料夾目錄。



然後設定完後在你的網站根目錄執行 composer dump-autoload

現在剩下的是將設定加入到在你的 app/config 檔案中,在 app/config/app.php 檔案中你會看到提供者 (Provider) 的陣列,在這個陣列加入你服務提供者的命名空間 (namespace)



之後我們需要在相同的檔案中註冊 Facade:



然後我們完成了

看看下面動作的範例

我們已經建立了資源庫的相關範例,而我們也必須要有資料庫 migration 去測試這個教學範例

$ php artisan migrate:make create_pokemon_table --create=pokemon


$ php artisan migrate

使用這些 migration 你可以簡單的建立一個像下面範例路由,嘗試得去用名稱或 ID 取得 pokemon



注意 Facades 的部分

這裡有些意見關於 Facades 不應該用在 Laravel 中,藉由使用 Facade 你會讓你的 Laravel 應用很特殊並難以與 Laravel 分離,有一個比較好的解決方法是在你的 Controller 注入你的服務(Services),你可以到這裡看更多相關的資訊。

結論

很好,我在這裡學了很多有關使用這些模式的理由,希望你也跟我一樣,使用資源庫(Repositories)及服務(Services)我們可以正確的解構我們的程式碼,讓我們的程式更容易維護、重複使用並變的更性感,所有這些模式都是我了讓我們的應用變得更好,我非常歡迎所有的評論及改善建議。

就如一開始所提到的,這個就是我喜歡的工作方式,也有很多可替代的方案,假如這個風格適合你的話,麻煩你讓我知道一下!

參考資料

沒有留言:

張貼留言