Larave框架下Contracts契約的解析

本篇文章給你們帶來的內容是關於Larave框架下Contracts契約的解析,有必定的參考價值,有須要的朋友能夠參考一下,但願對你有所幫助。php

Contracts數據庫

Laravel 的契約是一組定義框架提供的核心服務的接口, 例如咱們在介紹用戶認證的章節中到的用戶看守器契約IllumninateContractsAuthGuard 和用戶提供器契約IlluminateContractsAuthUserProvider框架

以及框架自帶的App\User模型所實現的IlluminateContractsAuthAuthenticatable契約。ide

爲何使用契約測試

經過上面幾個契約的源碼文件咱們能夠看到,Laravel提供的契約是爲核心模塊定義的一組interface。Laravel爲每一個契約都提供了相應的實現類,下表列出了Laravel爲上面提到的三個契約提供的實現類。this

契約 Laravel內核提供的實現類
IllumninateContractsAuthGuard IlluminateAuthSessionGuard
IlluminateContractsAuthUserProvider IlluminateAuthEloquentUserProvider
IlluminateContractsAuthAuthenticatable IlluminateFoundationAuthAuthenticatable(User Model的父類)

因此在本身開發的項目中,若是Laravel提供的用戶認證系統沒法知足需求,你能夠根據需求定義看守器和用戶提供器的實現類,好比我以前作的項目就是用戶認證依賴於公司的員工管理系統的API,因此我就本身寫了看守器和用戶提供器契約的實現類,讓Laravel經過自定義的Guard和UserProvider來完成用戶認證。自定義用戶認證的方法在介紹用戶認證的章節中咱們介紹過,讀者能夠去翻閱那塊的文章。spa

因此Laravel爲全部的核心功能都定義契約接口的目的就是爲了讓開發者可以根據本身項目的須要本身定義實現類,而對於這些接口的消費者(好比:Controller、或者內核提供的 AuthManager這些)他們不須要關心接口提供的方法具體是怎麼實現的, 只關心接口的方法能提供什麼功能而後去使用這些功能就能夠了,咱們能夠根據需求在必要的時候爲接口更換實現類,而消費端不用進行任何改動。設計

定義和使用契約code

上面咱們提到的都是Laravel內核提供的契約, 在開發大型項目的時候咱們也能夠本身在項目中定義契約和實現類,你有可能會以爲自帶的Controller、Model兩層就已經足夠你編寫代碼了,憑空多出來契約和實現類會讓開發變得繁瑣。咱們先從一個簡單的例子出發,考慮下面的代碼有什麼問題:接口

1

2

3

4

5

6

7

8

class OrderController extends Controller

{

    public function getUserOrders()

    {

        $orders= Order::where('user_id', '=', \Auth::user()->id)->get();

        return View::make('order.index', compact('orders'));

    }

}

這段代碼很簡單,但咱們要想測試這段代碼的話就必定會和實際的數據庫發生聯繫。也就是說, ORM和這個控制器有着緊耦合。若是不使用Eloquent ORM,不鏈接到實際數據庫,咱們就沒辦法運行或者測試這段代碼。這段代碼同時也違背了「關注分離」這個軟件設計原則。簡單講:這個控制器知道的太多了。 控制器不須要去了解數據是從哪兒來的,只要知道如何訪問就行。控制器也不須要知道這數據是從MySQL或哪兒來的,只須要知道這數據目前是可用的。

Separation Of Concerns 關注分離

Every class should have a single responsibility, and that responsibility should be entirely encapsulated by the class.

每一個類都應該只有單一的職責,而且職責裏全部的東西都應該由這個類封裝

接下來咱們定義一個接口,而後實現該接口

1

2

3

4

5

6

7

8

9

10

11

12

interface OrderRepositoryInterface

{

    public function userOrders(User $user);

}

 

class OrderRepository implements OrderRepositoryInterface

{

    public function userOrders(User $user)

    {

        Order::where('user_id', '=', $user->id)->get();

    }

}

將接口的實現綁定到Laravel的服務容器中

1

App::singleton('OrderRepositoryInterface', 'OrderRespository');

而後咱們將該接口的實現注入咱們的控制器

1

2

3

4

5

6

7

8

9

10

11

12

13

class UserController extends Controller

{

    public function __construct(OrderRepositoryInterface $orderRepository)

    {

        $this->orders = $orderRespository;

    }

   

    public function getUserOrders()

    {

        $orders = $this->orders->userOrders();

        return View::make('order.index', compact('orders'));

    }

}

如今咱們的控制器就徹底和數據層面無關了。在這裏咱們的數據可能來自MySQL,MongoDB或者Redis。咱們的控制器不知道也不須要知道他們的區別。這樣咱們就能夠獨立於數據層來測試Web層了,未來切換存儲實現也會很容易。

接口與團隊開發

當你的團隊在開發大型應用時,不一樣的部分有着不一樣的開發速度。好比一個開發人員在開發數據層,另外一個開發人員在作控制器層。寫控制器的開發者想測試他的控制器,不過數據層開發較慢無法同步測試。那若是兩個開發者能先以interface的方式達成協議,後臺開發的各類類都遵循這種協議。一旦創建了約定,就算約定還沒實現,開發者也能夠爲這接口寫個「假」實現

1

2

3

4

5

6

7

class DummyOrderRepository implements OrderRepositoryInterface

{

    public function userOrders(User $user)

    {

        return collect(['Order 1', 'Order 2', 'Order 3']);

    }

}

一旦假實現寫好了,就能夠被綁定到IoC容器裏

1

App::singleton('OrderRepositoryInterface', 'DummyOrderRepository');

而後這個應用的視圖就能夠用假數據填充了。接下來一旦後臺開發者寫完了真正的實現代碼,好比叫RedisOrderRepository。那麼使用IoC容器切換接口實現,應用就能夠輕易地切換到真正的實現上,整個應用就會使用從Redis讀出來的數據了。

接口與測試

創建好接口約定後也更有利於咱們在測試時進行Mock

1

2

3

4

5

6

7

8

9

10

11

12

13

public function testIndexActionBindsUsersFromRepository()

{   

    // Arrange...

    $repository = Mockery::mock('OrderRepositoryInterface');

    $repository->shouldReceive('userOrders')->once()->andReturn(['order1', 'order2]);

    App::instance('OrderRepositoryInterface', $repository);

    // Act...

    $response  = $this->action('GET', 'OrderController@getUserOrders');

         

    // Assert...

    $this->assertResponseOk();

    $this->assertViewHas('order', ['order1', 'order2']);

 }

總結

接口在程序設計階段很是有用,在設計階段與團隊討論完成功能須要制定哪些接口,而後設計出每一個接口具體要實現的方法,方法的入參和返回值這些,每一個人就能夠按照接口的約定來開發本身的模塊,遇到還沒實現的接口徹底能夠先定義接口的假實現等到真正的實現開發完成後再進行切換,這樣既下降了軟件程序結構中上層對下層的耦合也能保證各部分的開發進度不會過分依賴其餘部分的完成狀況。

相關文章
相關標籤/搜索