Laravel中的核心概念

本文最先發佈於 Rootrl的Blogphp

導言

Laravel是一款先進的現代化框架,裏面有一些概念很是重要。在上手Laravel以前,我認爲先弄懂這些概念是頗有必要的。你甚至須要重溫下PHP OOP知識。我相信不少人對好比getter setter以及__invoke、__call、__callStatic這些魔術方法甚至this、
self、static這些關鍵字做用都仍是很模糊的(我上一個老大喜歡問這種基礎問題,而後答不上來-_-')。html

DI & IoC

首先名詞解釋,DI全稱是Dependency injection,依賴注入的意思。而IoC是Inversion of control 控制反轉。laravel

要了解依賴注入和控制反轉,首先咱們不得不提到面向對象設計中的五大設計原則:S.O.L.I.D。git

S.O.L.I.D - 面向對象五大設計原則

SRP The Single Responsibility Principle 單一責任原則
OCP The Open Closed Principle 開放封閉原則
LSP The Liskov Substitution Principle 里氏替換原則
ISP The Interface Segregation Principle 接口分離原則
DIP The Dependency Inversion Principle 依賴倒置原則github

這五種思想原則對咱們日常的軟件開發設計很是重要,你們能夠具體去了解下。redis

依賴倒置原則

這裏咱們重點講下依賴倒置原則:實體必須依靠抽象而不是具體實現。它表示高層次的模塊不該該依賴於低層次的模塊,它們都應該依賴於抽象。設計模式

在傳統軟件設計中,咱們通常都是上層代碼依賴下層代碼,當下層代碼變更時,咱們上層代碼要跟着變更,維護成本比較高。這時咱們能夠上層定義接口,下層來實現這個接口,從而使得下層依賴於上層,下降耦合度。(PC主板和鼠標鍵盤接口就是一個很好的例子,各數據廠商根據主板上的接口來生產本身的鼠標鍵盤產品,這樣鼠標壞了後咱們能夠隨便換個符合接口要求的鼠標,而不用修改主板上的什麼東西)數組

控制反轉

上面講的依賴倒置是一種原則,而控制反轉就是實現依賴倒置的一種具體方法。控制反轉核心是把上層(類)所依賴單元的實例化過程交由第三方實現,而類中不容許存在對所依賴單元的實例化語句。舉個例子:緩存

class Comment
{
    ...

    public function afterInsert()
    {
        $notification = new EmailNotification(...);
        $notification->send(...);
    }
}

如上,假如咱們在用戶提交評論後通知被評論者,這裏通知方式是郵件,並且是直接在類中實例化郵件通知類,這樣代碼耦合度高,若是換個短信通知方式就不得不改這裏面代碼,具體好的實現咱們下面會講到。app

依賴注入

依賴注入是一種設計模式,是一種IoC的具體實現,實現了IoC天然就符合依賴倒置原則。依賴注入的核心思想是把類中所依賴單元的實例化過程放到類外面中去實現,而後把依賴注入進來。經常使用的依賴注入方式有屬性注入和構造函數注入。好比用構造函數注入解耦上面代碼:

// 通知接口
interface Notifaction
{
    public function send(...);
}


// 短信通知實現通知接口
class SmsNotification implements Notification
{
    public function send(...)
    {
        ...
    }
}

// 評論類
class Comment
{
    ...

    protected $notification;

    public function __construct(Notification $smsNotification)
    {
        $this->notification = $smsNotification;
    }

    public function afterInsert()
    {
        $this->notification->send(...);
    }
}

// 實例化短信通知類
$smsNotification = new SmsNotification(...);

// 經過構造函數方法注入
$comment = new Comment($smsNotification);

...

$comment->save();

這樣,咱們先定義Notification接口,裏面有個send方法,讓後面的通知者不論是郵件類仍是短信類都實現這個接口,而後在外面經過構造函數方式注入進來,這樣就解決了Comment類對具體通知方法的依賴,只要是實現了Notification接口的,均可以經過構造函數傳進來,Comment類徹底不用作任何修改。這樣不管對於代碼維護仍是單元測試(能夠模擬實現一個Notification類),都很是方便。

依賴注入是IoC的一種具體實現,是一種解耦手段。固然IoC不止這一種實現,好比Yii中的Service Locator(服務定位器)

IoC container/DI container

當項目比較大時,就會有許多相似上面評論類和通知類這種依賴關係,整個項目會很是複雜。這時候就須要一個集中的地方來管理這些依賴,咱們把它叫IoC container 控制反轉容器,它提供了動態地建立、注入依賴單元、映射依賴關係等功能。這樣能夠集中管理依賴關係,也減小不少代碼量。

Service container 服務容器

Laravel官方文檔這樣定義服務容器:Laravel服務容器是用於管理類的依賴和執行依賴注入的工具。

首先,服務容器經過DI依賴注入方式實現了IoC,而後它還支持另外一種實現:綁定與解析。

綁定

幾乎全部服務容器綁定操做都是Service provider(服務提供器)中註冊綁定的,服務提供器中能夠經過$this->app方式獲取服務容器,而後經過服務容器提供的方法好比$this->app->bind(...)等進行具體服務綁定。相似支持的綁定方式還有:

  • 簡單綁定
  • 綁定單例
  • 綁定實例
  • 綁定初始數據
  • 綁定接口到實現
  • 上下文綁定
  • 標記
  • 擴展綁定

具體能夠查看官方文檔:https://laravel.com/docs/5.6/...

解析

綁定後能夠從服務容器中解析出對象纔可以使用。解析方法包括:

  • 經過 make 方法,接收一個你想要解析的類或者接口
  • 經過數組方式從容器中解析對象
  • 自動注入
示例

咱們先定義一個本身的類

class Foo
{
    public function bar()
    {
        ...
    }
}

咱們把Foo類簡單綁定到服務容器:

App::bind("foo", function($app){
    return new Foo();
})

平時在上下文獲取這個實例:

$foo = App::make("foo");    // $foo就是Foo類的實例

固然,這種綁定和解析平時咱們在代碼中隨即可以寫到哪裏,可是多了的話就亂起來了。因此我開頭說幾乎全部這種依賴服務綁定操做都是在Service provider中進行的。

下面就給你們介紹Service provider。

Service provider 服務提供器

爲了讓依賴注入的代碼不至於混亂,Laravel提供了一個服務提供器(Service Provider),它將這些依賴彙集在了一塊,統一申明和管理,讓依賴變得更加容易維護。

下面都是一些抄來的官話、套話(-_-'),你們能夠直接跳到代碼示例,後續再查看官方文檔加深理解。

全部服務提供者都須要繼承IlluminateSupportServiceProvider類。大多數服務提供者都包含 register 和 boot 方法。register方法中,只能將事務綁定到服務容器。不該該在register方法中嘗試註冊任何事件監聽器,路由或者任何其餘功能。能夠爲服務提供者的boot方法設置類型提示。服務容器會自動注入須要的任何依賴。boot方法將在全部其餘服務提供者均已註冊以後調用。

全部服務提供者都在 config/app.php 配置文件中註冊。能夠選擇推遲服務提供者的註冊,直到真正須要註冊綁定時,這樣能夠提供應用程序的性能。

示例

上一個示例咱們是本身在上下文中隨意定義、獲取。下面咱們以服務提供者的方式進行:

use Illuminate\Support\ServiceProvider;

class FooServiceProvider extends ServiceProvider {

    public function register()
    {
        $this->app->bind('foo', function()
        {
            return new Foo();
        });
    }

}

上面實現了一個Foo的服務提供,咱們能夠手動注入到上下文中:

App::register('FooServiceProvider');

固然咱們更多的是經過配置文件來完成的,在app/config/app.php中的providers數組裏面增長一行:

'providers' => [
    …
       ‘FooServiceProvider’,
],

這樣咱們能夠在上下文中直接獲取實例:

App::make(‘foo’)

固然,咱們還能夠經過門面方式,更方便的操做Foo類。

Facades 門面

門面其實是應用了設計模式中的外觀模式:

外觀模式(Facade),他隱藏了系統的複雜性,並向客戶端提供了一個能夠訪問系統的接口。這種類型的設計模式屬於結構性模式。爲子系統中的一組接口提供了一個統一的訪問接口,這個接口使得子系統更容易被訪問或者使用。

Laravel中隨處可見這些靜態方法的調用:

$value = Cache::get('key');

這些靜態調用實際上調用的並非靜態方法,而是經過PHP的魔術方法 __callStatic() 將請求轉到了相應的方法上。

好比若是咱們看一下 IlluminateSupportFacadesCache 這個類,你會發現類中根本沒有 get 這個靜態方法:

class Cache extends Facade
{
    /**
     * 獲取組件的註冊名稱。
     *
     * @return string
     */
    protected static function getFacadeAccessor() { return 'cache'; }
}

這其中的奧祕在基類Facade中:

public static function __callStatic($method, $args)
{
    // 獲取實例
    $instance = static::getFacadeRoot();

    if (!$instance) {
        throw new RuntimeException('A facade root has not been set.');
    }

    // 真正調取對應的方法
    return $instance->$method(...$args);
}

這裏面有一個獲取實例的過程,而後去調用具體方法。

示例

接上一個示例,咱們日常是經過App::make('foo')來獲取實例,而後再調用具體方法。如今咱們經過門面的方式簡化這個流程:

先定義一個門面:

use Illuminate\Support\Facades\Facade;

class Foo extends Facade {

    protected static function getFacadeAccessor() { return ‘foo’; }

}

而後咱們能夠很方便的使用Foo類某個方法:

Foo::bar();

Contracts 契約

Laravel的契約是一組定義框架提供的核心服務的接口。後續針對這個接口能夠有多種實現,解耦了具體實現的依賴,在不改變代碼邏輯的狀況下得到更加多態的結果。

好比你只需在配置文件中指明你須要的緩存驅動(redis,memcached,file等),Laravel會自動幫你切換到這種驅動,而不須要你針對某種驅動更改邏輯和代碼。

總結

這些都是些基礎的抽象概念,可是是很是重要的,Laravel中隨處可見這些思想,是一切實現的基石。

學習的過程當中基礎是很是重要的,知其然必知其因此然。就像道與術,道是在術以前的,老子說過:」有道無術,術尚可求也,有術無道,止於術「。不過實際中應該是相輔相成的關係,「以道統術,以術得道」。

引用

https://laravel.com/docs/5.6/...
https://laravel-china.org/doc...
http://www.digpage.com/di.html
http://yansu.org/2014/12/06/i...

相關文章
相關標籤/搜索