Info: Bài viết được lấy từ nguồn https://github.com/petehouston/laravel-docs-vn

Contracts

Giới thiệu

Laravel Contracts là một set các interfaces khai báo các core services cung cấp bởi framework. Ví dụ, contract Illuminate\Contracts\Queue\Queue khai báo các phương thức cần thiết cho các job xử lý hàng đợi, còn 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 triển khải tương ứng bởi framework. Ví dụ, Laravel cung cấp một triển khai về hàng đợi với các drivers khác nhau, và một triển khải cho mailer được cung cấp bởi SwiftMailer.

Tất cả các Laravel Contracts đều nằm trong repository riêng của chúng trên Github. Điều này làm cho việc tìm hiểu tất cả các Contracts có sẵn một cách tiện lợi, 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 Vs. Facades

Laravel facades cung cấp một cách thuận tiện để sử dụng Laravel services mà không cần tới type-hint và resolve contracts khỏi container. Tuy nhiên, sử dụng contact cũng cho phép bạn có thể khai báo các dependencies ngoài vào trong class. Trong hầu hết các ứng dụng, chỉ sử dụng facade không có vấn đề gì. Nhưng nếu bạn thực sự cần tạo ra sự liên kết lỏng lẻo mà các contract cung cấp, hãy đọc thêm ở phần dưới đây.

Vì sao cần Contracts?

Có thể bạn đang đặt vài câu hỏi về Contracts. 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: mối liên kết lỏng lẻo và tính đơn giản.

Mối liên kết lỏng lẻo

Đầu tiên, cùng nhau review mã nguồn bị liên kết chặt cho một triển khai cho cache. 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 triển khai cache. 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ì triển khai và dependencies nào, bạn có thể dễ dàng viết một triển khai của bất kì contract nào, điều này cho phép bạn thay đổi triển khai cache mà không phải thay đổi mã nguồn của đoạn sử dụng cache nữa.

Tính đơn giản

Khi mà tất cả Laravel services đều được khai báo trong các interface đơn giản, mọi thứ se 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.

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 | Facade Tương ứng ------------- | ------------- 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 |  

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

Vậy thì làm thế nào để triển khai 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, bao gồm controllers, event listeners, middleware, queued jobs, và thậm chí các route trong Closures. Vì thế, để lấy một triển khai 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\NewUserRegistered;
use Illuminate\Contracts\Redis\Database;

class CacheUserInformation
{
    /**
     * 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  NewUserRegistered  $event
     * @return void
     */
    public function handle(NewUserRegistered $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 service container.