Laravel 服務容器必知必會

文章轉發自專業的Laravel開發者社區,原始連接: https://learnku.com/laravel/t...

學習如何用 Laravel 構建一個應用程序,不只僅是學習使用不一樣的類和框架中的組件,也不是要記住所有的 artisan 命令或全部的輔助函數(咱們有 Google)。學習用 Laravel 編碼是學習它的哲學和優雅迷人的語法。 我我的以爲是一件藝術和工藝品(巧合的是 Laravel 工程師有時也被稱做 Web 藝術家)。對其餘框架這也是真理。php

服務容器和 IOC 容器是 Laravel 哲學的主要部分。做爲一個 Laravel 開發者,理解並能正確的使用服務容器是你掌握它的重要部分, 由於它是任何 Laravel 應用的核心。laravel

基礎

雖然 IOC 容器本質上只是一個普通的 PHP 類, 可是我喜歡將它看作"袋中的技巧"。 這個"袋子"就是咱們放置或者"綁定"任何咱們須要運行在 Laravel 應用中的對象服務, 從接口實現到目錄路徑以及其餘等等。所以叫作"袋中的技巧"編程

如今咱們擁有了一個包含全部綁定對象服務的單一對象( IOC 容器), 所以在咱們的代碼中,任什麼時候候均可以很容易的從這個單一對象中獲取或者"解析"這些對象服務app

綁定的處理方式

如今假設咱們有一個特別功能的 FooService 類。框架

<?php
namespace App\Services;
class FooService
{
    public function __construct()
    {
        ...
    }
    public function doSomething()
    {
        // Code for Something.
    }
}

若是咱們要調用類的 doSomething 方法,咱們可能會這樣作 :函數

$fooService = new \App\Services\FooService();\
$fooService->doSomething();

這看起來沒有什麼問題,但比較麻煩的是這兒的 'new' 關鍵字,個人意思是雖然這樣也很好,可是咱們能夠作的更優雅 (記住寫代碼要像 Laravel 同樣,用優雅的方式)。學習

如何綁定 ?

綁定簡單得能夠用一行代碼完成測試

$this->app->bind('FooService', \App\Services\FooService::class);

在 Laravel 中咱們常說:「把 FooService 服務巧妙的注入到包中」。this

固然根據使用場景和服務方式,也有其餘的方法來綁定服務,只要你理解它的基本思想。有關綁定的完整參考,能夠查閱 Laravel 的文檔 服務容器編碼

須要注意的是,服務必須綁定到服務提供商的註冊方法中。

如何解析 ?

當服務綁定到容器以後, 咱們能夠在應用中的任何地方獲取或者解析服務.

// 使用IoC 咱們能夠這麼作
$fooService = app()->make('FooService');
$fooService->doSomething();
// 也能夠精簡爲一行代碼
app()->make('FooService')->doSomething();

咱們只須要告訴 Laravel : "記住 FooService, 當我須要時把它給我." 你注意到了嗎? 用 IoC 建立服務讓代碼更簡潔, 明瞭, 易讀. 這就是 Laravel 的優雅之處, 而且會使你的代碼更易於測試, 由於當你測試時你可使用一個僞造的類去替換 FooService (我以爲你應該很熟悉怎麼在測試中僞造類).

在容器中綁定接口

在面向對象編程中,接口是建立一些必須遵循某種規劃或者約束的類的一種方法。這能幫助其餘開發者建立與您的接口中設置的約束相匹配的代碼。這強制他們傳遞合法的參數給函數而且返回特定的數據類型,儘管他們的方法的實現可能有所不一樣。經過這種方式,您能夠輕鬆的肯定繼承相同接口的不一樣實現將以相同的方式工做。

在 Laravel 的容器中咱們可以綁定一個特定的接口的實現,經過這種方式,當咱們解析這個接口時,咱們最終會獲得綁定到它的具體類。

$this->app->bind(FooInterface::class, FooClass::class);

所以,當 FooInterface 被成功解析,Laravel 足夠聰明的給咱們一個 FooClass 的實例。

如今想象一下咱們已經寫了一個 FooInterface 的更好的實現叫作 BarClass,而且咱們但願用它替換 FooClass,咱們所須要作的全部事情就是:

$this->app->bind(FooInterface::class, BarClass::class);

咱們的代碼依舊正常運行由於咱們知道 BarClass 會遵循咱們的接口,就算 BarClass 和預期的表現不一致咱們也能夠切換回 FooClass 。這是一種很好的方法,能在沒有太多回歸(regressions)的狀況下升級應用的代碼。

依賴解析

咱們知道 Laravel 可以解析咱們在容器中綁定的服務和接口,可是它能作的不只僅是這些。事實上,它還能在咱們一行代碼都不寫的狀況下自動爲咱們解析這些服務的依賴。

想象一下咱們的項目中有下面這些服務類。

<?php
class BarService 
{
  /**
   * 要作的事情。
   * 
   * @return string
   */
  public function somethingToDo()
  {
    return 'I am doing something';
  }
}
<?php
class FooService 
{
  /**
   * BarService 實例.
   * 
   * @var BarService
   */
  protected $bar;
  
  /**
   * 建立新的 FooService 實例
   * 
   * @param BarService $bar
   */
  public function __construct(BarService $bar)
  {
    $this->bar = $bar;
  }
  
  /**
   * 作點有用的事
   * 
   * @return string
   */
  public function doSomething()
  {
    return $this->bar->somethingToDo();
  }
}

咱們能看到 FooService 須要一個 BarService 的實例。咱們怎麼才能把它綁定到容器中這樣當 Laravel 給咱們一個 FooService 的實例時它也會給咱們一個BarService 的實例?

你或許會想這個解決方法可能會是下面這樣:

$this->app->bind('Foo', new FooService(new BarService));

從技術上來講,這是能夠實現的,但實際上咱們不須要這樣作。 Laravel 經過使用 PHP 強大的反射特性,自動就爲咱們解決了這個問題。所以,你只須要像往常那樣綁定就好:

$this->app->bind('Foo', FooService::class);

如今,當代碼解析到 Foo 時, Laravel 將會去尋找 FooService 服務,當發現它須要一個 BarService 服務的實例; Laravel 又將會去尋找 BarService 服務,並將它實例化以後的對象提供給 FooService 服務的構造函數,從而爲咱們建立一個完整的實例。這些過程不須要咱們寫一行代碼,真的是十分驚豔而又清晰的思路!!

除此以外,上述過程將會提供給全部的依賴。因此,若是 BarService 服務也有本身的依賴,那麼也會經過上述的方式來解決。

最後的話

關於 Laravel 的服務容器還有許多很棒的事情須要討論和學習。我但願這個小介紹能給你一些啓發並幫助你加深對容器的理解。

我鼓勵你經過閱讀文檔瞭解更多  [docs]
( https://laravel.com/docs/5.6/... )。

感謝你的閱讀。

相關文章
相關標籤/搜索