Laravel深刻學習5 - 應用架構

聲明:本文並不是博主原創,而是來自對《Laravel 4 From Apprentice to Artisan》閱讀的翻譯和理解,固然也不是原汁原味的翻譯,能保證90%的原汁性,另外由於是理解翻譯,確定會有錯誤的地方,歡迎指正。php

歡迎轉載,轉載請註明出處,謝謝!程序員

應用架構

簡介

這一章是哪齣戲?對於使用框架建立應用這是很是廣泛的。不少開發者會提出這樣的問題,由於在他們腦仁裏已經存在這樣的觀念,「模型」就是「數據庫」。因此一般,控制器被用來和HTTP交互,模型就是和數據庫_打交道_,視圖就是還有HTML代碼的那部分。可是,對於那些好比發送郵件的類、驗證數據類、訪問接口的類該怎麼區分呢?本章咱們就使用Laravel構建好的架構進行探討,打破那些固話在你心中的概念,讓開發迴歸本質。數據庫

MVC會弄死你的

阻礙咱們的一種設計即:M-V-C。模型,視圖,控制器,這種框架思惟已經控制開發人員不少年了。這種思惟來源於Ruby On Rails。若是,讓一個程序員去解釋什麼是「模型」,一般都會聽到將其和「數據庫」關聯的答案。聽說,模型_就是_數據庫。模型包含了數據庫的一切。可是,很快你就會發現,在簡單的數據庫訪問類之上還有不少額外的邏輯。他須要咱們進行數據驗證,調取額外的服務,發送郵件,等等。json

什麼是模型?數組

模型如今已經變的模棱兩可,很難具體指代什麼。根據開發中遇到的那麼多詞彙,咱們能夠理解認爲,他就是爲了將應用切分紅小而清晰,具備特定職責的類。網絡

那麼,這種困境中的解決方案是什麼?不少開發人員會在控制器之上添加更多的邏輯。當控制器變的很大的時候,須要複用其餘控制器中的一些邏輯層。不少人會錯誤的認爲_須要_在當前控制器調用其餘控制器,而不是講邏輯抽象成單獨的類。這種模式一般稱爲「HMVC」。不幸的是,這也是糟糕的設計,一般控制器會很複雜。架構

HMVC(一般)預示着糟糕的設計app

當以爲需要在控制器中調用其餘控制器?這意味着當前設計是糟糕的,控制器裏面的業務邏輯太負責。咱們能夠講邏輯抽象成通用類,以便在其餘控制器中進行調用。composer

總會有更好的程序設計。咱們須要忘記之前在腦海中殘留的那種「模型」的設計理念,乾脆讓咱們刪除模型目錄,並從新開始。框架

再見,模型

是否已經把你的models目錄刪除?若是沒有,不要緊,別再去理他。咱們來在app下創建一個新文件夾,簡單的起個應用名字便可QuickBill,在後續的討論中,咱們以前舉例的那些例子都會出現。

注意使用場景

記住,若是你建立的是小型的Laravel應用,在models下建立幾個Eloquent模型仍是很合適的。而在本章中,咱們關注的是擁有更多「層次」架構的複雜應用。

咱們已經有了app/QuickBill目錄,他和controllersviews目錄同級。咱們能夠在QuickBill下建立一些其餘目錄,好比RepositoriesBilling目錄。建好目錄以後,記得在composer.json註冊PSR-0自動加載。

"autoload": {
    "psr-0":    {
        "QuickBill":    "app/"
    }
}

如今,咱們把Eloquent類放到QuickBill根目錄下,咱們就能輕鬆的訪問到QuickBillUser,以及QuickBillPayment等。在Respositories目錄中建立PaymentRepositoryUserRepository類,並編碼數據訪問的方法getRecentPayments以及getRichestUser。在Billing目錄則包含使用第三方服務,諸如「Stripe」、「Balanced」的類和接口。上述目錄結構以下:

// app
    // QuickBill
        // Repositories
            -> UserRepository.php
            -> PaymentRepository.php
        // Billing
            -> BillerInterface.php
            -> StripeBiller.php
        // Notifications
            -> BillingNotifierInterface.php
            -> SmsBillingNotifier.php
        User.php
        Payment.php

數據驗證放哪

這個問題一般讓咱們頭大。能夠考慮把他們放到「實體」類中,好比User.php或者Payment.php中,方法名能夠叫:validForCreationhasValidDomain。或者也能夠建立一個命名空間爲ValidationUserValidator類,並注入到repository類中。兩種方法看我的喜愛。

擺脫models目錄,咱們就能衝破枷鎖,實現好的設計。固然,咱們建立的不少項目都會有類似之處,不管多麼複雜的項目也都會有數據接入(存儲)層,以及其餘服務層等等。

不要懼怕文件夾

不要由於多建文件夾而懼怕,他是組織應用程序的很好方式。一般咱們但願以此講應用分割成很小的組件,每一個組件都有本身特定的職責。別被「模型」束縛了思惟。就像上面舉的例子,咱們能夠建立一個Repository來存放全部數據接入的相關類。

到處皆分層

你應該已經注意到,好的應用設計應擁有明確的職責劃分,有明確的邏輯分紅。控制只是用來接收HTTP請求並請求邏輯處理類。業務處理層_纔是_整個應用的中心。它包含了像數據獲取,數據驗證,支付處理,郵件發送類庫,以及應用中的各類函數等。事實上,業務邏輯無需感知「網絡」,網絡僅僅接入應用的傳輸機制,他不該超出應用中的路由和控制器的範疇。好的架構是經得起考研的,是由清晰代碼組成的可持續發展的架構。

例如,咱們使用向控制器中傳入網絡請求輸入來替代在類中直接訪問網絡請求實例的方法。簡單的改動即將類從「網絡」中解耦出來,這種方式也不用擔憂在測試時對請求的再次模擬了:

class BillingController extends BaseController{
    public function __construct(BillerInterface $biller)
    {
        $this->biller = $biller;
    }
    public function postCharge()
    {
        $this->biller->chargeAccount(Auth::user(), Input::get('amount'));
        return View::make('charge.success');
    }
}

chargeAccount方法能夠很容易進行測試,只需傳入測試數據用到的整型數據,而不是使用一個含有RequestInput類的BillerInterface接口的實現類庫來做爲參數傳入該方法。

職責分離是編寫健壯應用的關鍵。這種關鍵就是一個類是否管的太多。你應該時常問本身:「是否是這個類還要關心X?」,若是答案是「不」,就將邏輯抽象出來,並用依賴注入的方式處理。

改變的緣由很簡單

決定類庫是否足夠職責分離的一個很是有用的方法就是檢驗本身爲何要更改這些代碼。好比,在調整通知邏輯的時候,是否Biller接口的實現也要修改?固然不,Biller的實現只和支付有關,只需按照約定和通知邏輯交互。保持這樣的思惟觀念,就能幫你快速改進應用中的各個部分,使之變得健壯起來。

「瓶瓶罐罐」都放哪

當使用Laravel開發應用的時候,會常常有這樣的疑問,不少「東西」不知道放哪。好比,「helper」函數放哪?事件堅挺程序放哪?視圖組件又該在哪?答案可能會讓你凌亂:「哪均可以」!Laravel沒有文件該歸屬哪裏的概念。然而這個答案並非讓人滿意的,在繼續深刻以前,讓咱們先就上面的問題探討下。

輔助函數

Laravel的輔助函數放在support/helpers.php文件中。或者你也想建立這樣一個本身的輔助函數文件,「start」目錄就是個不錯的地方。在請求應用時,start/global.php都會被引用到,咱們能夠在這添加上加載本身的helpers.php文件:

// Within app/start/global.php

require_once __DIR__.'/../helpers.php';

事件監聽器

事件監聽器固然不能屬於routes.php文件,放在start文件也不合適,咱們要另擇地方安放他。服務提供器的目錄就不錯,以前咱們知道,服務提供不只是容器註冊綁定的服務,還能夠作不少其餘事情。它能夠講不少監聽器組織起來,這種方式是代碼清理整潔,也不影響應用邏輯。視圖組件也能夠放到這個位置,他和監聽器實際上是相似的,都能收納在服務提供器中。

好比,用服務提供器組織監聽器:

<?php namespace QuickBillProviders;

use IlluminateSupportServiceProvider;

class BillingEventsProvider extends ServiceProvider{

    public function boot()
    {
        Event::listen('billing.failed', function($bill)
        {
            // Handle failed billing event...
        });
    }
}

建立完提供器後,只須要簡單的在app/config/app.php配置中providers數組添加上它就好。

注意boot方法

記住,上例中咱們使用boot的緣由,register方法僅僅是用來將服務註冊到容器的方法。

錯誤處理

若是應用中咱們自定義了錯誤處理,別接管在「start」文件中,一樣,像事件監聽器同樣,最好還放在服務提供器中進行組織。提供器能夠像這樣命名QuickBillErrorProvider,並在boot方法中講全部自定義的錯誤處理註冊進來,重申一下:咱們要將這些代碼和咱們的邏輯分離開來。最終,自定義的錯誤處理程序以下:

<?php namespace QuickBillProviders;

use App, IlluminateSupportServiceProvider;

class QuickBillErrorProvider extends ServiceProvider {

    public function register()
    {    
        //
    }

    public function boot()
    {
        App::error(function(BillingFailedException $e)
        {
            // Handle failed billing exceptions ...
        });
    }
}

簡潔的方案

固然在只有一兩個錯誤處理方式的狀況下,把他放到「start」文件也是一種簡潔的方式。

其餘

一般,類庫應該以PSR-0規範組織在咱們的應用中。命令式代碼如事件監聽、錯誤處理、以及其餘「註冊」類型的服務最好組織在服務提供器中。基於如上原則,咱們就能決策出代碼的組織規律。有一點不要太猶豫,Laravel是爲了讓工做方便於咱們的業務,這也是Laravel的宗旨。尋找適合本身應用的結構,並分享給其餘人。

如上,咱們能夠爲全部自定義的服務提供器添加一個命名空間Providers並建立目錄組織起來:

// app
    // QuickBill
        // Billing
        // Extensions
            //Pagination
                -> Environment.php
        // Providers
            -> EventPusherServiceProvider.php
        // Repositories
        User.php
        Payment.php

上例中,有兩個命名空間ExtensionsProviders,自定義的服務放到Providers目錄下,對框架擴展的組件以Extensions命名空間的方式組織到同名目錄下。

相關文章
相關標籤/搜索