Contracts

Giới thiệu

Contracts của Laravel là một set các interfaces khai báo các core services cung cấp bởi framework. Ví dụ, một contractIlluminate\Contracts\Queue\Queue khai báo các phương thức cần thiết cho các queueing jobs, trong khi Illuminate\Contracts\Mail\Mailer khai báo các phương thức cần thiết để gửi email.

Mỗi contract đều có sẵn một corresponding implementation bởi framework. Ví dụ, Laravel pcung cấp một queue implementation với các drivers khác nhau, và một mailer implementation được cung cấp bởi SwiftMailer.

Tất cả các Contracts của Laravel đều nằm trong their own GitHub repository. Nó cung cấp tài liệu nhau cho tất cả những thứ của contracts, cũng như việc áp dụng để phát triển riêng bởi các nhà phát triển package.

Contracts với Facades

facades của Laravel và các hàm helper cung cấp một cách thuận tiện để sử dụng services của Laravel mà không cần type-hint và resolve contracts khỏi service container. Trong hầu hết các trường hợp, mỗi điểm của facede đều có điểm tương đồng với contract.

Không giống với facades, contacts không cần yêu cầu bạn phải thêm chúng trong class constructor, contractsHầu cho phép bạn có thể khai báo các dependencies ngoài vào trong class. Một số nhà phát triển thích xác định rõ ràng các dependencies của họ theo cách này và do đó họ thích dùng contracts hơn, trong khi những nhà phát triển khác lại thích sự tiện lợi của facades.

Hầu hết ứng dụng đều ổn cả dù bạn sử dụng facades hoặc contracts. Tuy nhiên, nếu bạn xây dựng một package,bạn nên cân nhắc sử dụng contracts vì nó dễ dàng cho việc test trong package context.

Khi nào sử dụng contracts

Như đã thảo luận, có nhiều quyết định nên sử dụng contracts hoặc facades sẽ trở thành sở thích cá nhân hoặc team. Cả contracts và facades có thể sử dụng để tạo nên sức mạnh, được kiểm tra ứng dụng Laravel. Miễn là bạn đang sử dụng tập trung vào class' responsibilities, bạn sẽ chú ý vào đặc điểm khác biệt giữa sử dụng contracts và facades.

Tuy nhiên, bạn có thể vẫn có mội số câu hỏi liên quan đến contracts. Ví dụ, tại sao sử dụng interfaces? Không phải là việc sử dụng interfaces làm cho mọi thứ trở nên phức tạp? Vậy hãy cùng nhau đi khai phá lý do để sử dụng interface theo hai tựa đề sau: loose coupling và simplicity.

Loose Coupling

Đầu tiên, cùng nhau review mã nguồn bị liên kết chặt cho một cache implementation. Xem đoạn code dưới đây:

<?php

namespace App\Orders;

class Repository
{
    /**
     * The cache instance.
     */
    protected $cache;

    /**
     * Create a new repository instance.
     *
     * @param    \SomePackage\Cache\Memcached  $cache
     * @return  void
     */
    public function __construct(\SomePackage\Cache\Memcached $cache)
    {
        $this->cache = $cache;
    }

    /**
     * Retrieve an Order by ID.
     *
     * @param    int  $id
     * @return  Order
     */
    public function find($id)
    {
        if ($this->cache->has($id))    {
            //
        }
    }
}

Trong class này, mã nguồn bị liên kết khá chặt vào một phương thức cache implementation. Nó bị liên kết chặt là do chúng ta đang phụ thuộc vào lớp Cache từ một package. Nếu API của package này thay đổi, mã nguồn của chúng ta cũng bắt buộc phải thay đổi theo.

Cũng như vậy, nếu chúng ta muốn thay đổi nền tảng cache ở phía dưới (Memcached) bằng một nền tảng khác (Redis), chúng ta lại phải thay đổi mã nguồn trong repository. Mã nguồn của repository không nên biết quá rõ về việc ai cung cấp dữ liệu và cung cấp như thế nào.

Thay vì làm như vậy, chúng ta có thể cải thiện mã nguồn bằng cách sử dụng interface.

<?php

namespace App\Orders;

use Illuminate\Contracts\Cache\Repository as Cache;

class Repository
{
    /**
     * The cache instance.
     */
    protected $cache;

    /**
     * Create a new repository instance.
     *
     * @param    Cache  $cache
     * @return  void
     */
    public function __construct(Cache $cache)
    {
        $this->cache = $cache;
    }
}

Lúc này mã nguồn không bị phụ thuộc quá nhiều vào bất cứ package nào cả, thậm chí cả Laravel. Vì contracts của package không có chứa bất kì implementation và dependencies nào, bạn có thể dễ dàng viết một implementation của bất kì contract nào, điều này cho phép bạn thay đổi implementation cache mà không phải thay đổi mã nguồn của đoạn sử dụng cache nữa.

Simplicity

Khi mà tất cả services của Laravel đều được khai báo trong các interface đơn giản, mọi thứ sẽ trở nên rất dễ dàng khi tìm chức năng cung của một service. Contract được sử dụng như một bản tài liệu cho các chức năng của framework.

Thêm vào đó, khi bạn phụ thuộc vào các interface đơn giản, mã nguồn sẽ trở nên dễ hiểu và dễ bảo trì hơn. Thay vì theo dõi các phương thức nào có thể sử dụng trong một class lớn và phức tạp, bạn có thể tham khảo tới cấu trúc đơn giản của interface.

Sử dụng contracts như thế nào

Vậy thì làm thế nào để implementation một contract? Sự thực là rất đơn giản.

Nhiều kiểu class trong Laravel được resolve qua các service container, gồm controllers, event listeners, middleware, queued jobs, và even route Closures. Vì thế, để lấy một implementation của một contract, bạn chỉ cần "type-hint" interface trong hàm khởi tạo của class đang được resolve.

Xem ví dụ dưới đây về event listener:

<?php

namespace App\Listeners;

use App\User;
use App\Events\OrderWasPlaced;
use Illuminate\Contracts\Redis\Database;

class CacheOrderInformation
{
    /**
     * The Redis database implementation.
     */
    protected $redis;

    /**
     * Create a new event handler instance.
     *
     * @param    Database  $redis
     * @return  void
     */
    public function __construct(Database $redis)
    {
        $this->redis = $redis;
    }

    /**
     * Handle the event.
     *
     * @param    OrderWasPlaced  $event
     * @return  void
     */
    public function handle(OrderWasPlaced $event)
    {
        //
    }
}

Khi mà event listener được resolve, thì service container sẽ đọc phần type-hint trên hàm khởi tạo của class, và inject giá trị phù hợp vào. Để tìm hiểu thêm về đăng kí vào trong service container, hãy xem mục its documentation.

Tham khảo về contract

Đây là bản tham chiếu tới các Contract của Laravel cũng như các phần facade tương ứng:

Contract References Facade
Illuminate\Contracts\Auth\Factory Auth
Illuminate\Contracts\Auth\PasswordBroker Password
Illuminate\Contracts\Bus\Dispatcher Bus
Illuminate\Contracts\Broadcasting\Broadcaster  
Illuminate\Contracts\Cache\Repository Cache
Illuminate\Contracts\Cache\Factory Cache::driver()
Illuminate\Contracts\Config\Repository Config
Illuminate\Contracts\Container\Container App
Illuminate\Contracts\Cookie\Factory Cookie
Illuminate\Contracts\Cookie\QueueingFactory Cookie::queue()
Illuminate\Contracts\Encryption\Encrypter Crypt
Illuminate\Contracts\Events\Dispatcher Event
Illuminate\Contracts\Filesystem\Cloud  
Illuminate\Contracts\Filesystem\Factory File
Illuminate\Contracts\Filesystem\Filesystem File
Illuminate\Contracts\Foundation\Application App
Illuminate\Contracts\Hashing\Hasher Hash
Illuminate\Contracts\Logging\Log Log
Illuminate\Contracts\Mail\MailQueue Mail::queue()
Illuminate\Contracts\Mail\Mailer Mail
Illuminate\Contracts\Queue\Factory Queue::driver()
Illuminate\Contracts\Queue\Queue Queue
Illuminate\Contracts\Redis\Database Redis
Illuminate\Contracts\Routing\Registrar Route
Illuminate\Contracts\Routing\ResponseFactory Response
Illuminate\Contracts\Routing\UrlGenerator URL
Illuminate\Contracts\Support\Arrayable  
Illuminate\Contracts\Support\Jsonable  
Illuminate\Contracts\Support\Renderable  
Illuminate\Contracts\Validation\Factory Validator::make()
Illuminate\Contracts\Validation\Validator  
Illuminate\Contracts\View\Factory View::make()
Illuminate\Contracts\View\View  
Nguồn: https://laravel.com/docs/5.3/contracts