Laravel: Tìm hiểu về Contracts

Tìm hiểu về Contracts trong Laravel, kết hợp trong thiết kế website chuyên nghiệp, tăng hiệu xuất và tốc độ website. Thiết kế website bằng laravel cần am hiểu về cách sửa dụng Contracts cũng như tối ưu Query nhằm toăn hiệu xuất hoạt động và tốc độ web trên laravel.

Bài viết được tổng hợp từ nhiều nguồn trên internet, được chúng tôi biên soạn lại để dễ hiểu hơn và mục đích chính là chia sẻ kiến thức tới cộng đồng lập trình viên trong nước.

Giới thiệu về Contracts trong Laravel

Contracts của Laravel là một bộ các interfaces định nghĩa các core services cung cấp bởi framework. Ví dụ, một contract 

Illuminate\Contracts\Queue\Queue định nghĩa các phương thức cần thiết cho các queueing jobs,.
Trong khi Illuminate\Contracts\Mail\Mailer định nghĩa các phương thức cần thiết để gửi email.

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

Tất cả các Contracts của Laravel đều nằm trong . Nó cung cấp một tài liệu tham khảo nhanh cho tất cả những các contracts có sẵn, cũng như một package tách lẻ có thể được sử dụng bởi các package developer.

So sánh giữa Contracts với Facades

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 một facede đều có một contract tương ứng.

Không giống với facades, contacts không yêu cầu bạn phải thêm chúng trong class constructor, contracts cho phép bạn khai báo các dependencies tường mình cho class của bạn. Một số developer muốn 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 các developer khác lại thích sự tiện lợi của facades.

Khi nào nên 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ủa cá nhân hoặc team development. Cả contracts và facades có thể sử dụng để tạo ra các ứng dụng Laravel mạnh mẽ, được thử nghiệm tốt. Miễn là bạn đang tập trung vào class responsibilities, bạn sẽ nhận thấy rất ít sự 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 hơn? Vậy hãy tìm hiểu lý do sử dụng interface theo hai tiêu đề sau: loose coupling và simplicity.

Loose Coupling

Coupling đề cập đến vấn đề phụ thuộc lẫn nhau giữa các component. Low coupling, loose coupling có nghĩa là các component ít phụ thuộc vào nhau, sự thay đổi trong component này ít khi, hoặc không ảnh hưởng đến component kia.

Ngược lại, high coupling và tight coupling cho thấy các component phụ thuộc nhiều vào nhau, khi thay đổi 1 component thì các component kia đều bị ảnh hưởng và có khả năng phải thay đổi theo.

Đầu tiên, cùng nhau review một số code được kết hợp chặt chẽ với 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, code được kết hợp chặt chẽ với một cache implementation. Nó được liên kết chặt chẽ bởi 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.

Tương tự như vậy, nếu chúng ta muốn thay đổi công nghệ cache cơ bản (Memcached) bằng một công nghệ khác (Redis), chúng ta lại phải thay đổi repository của chúng ta. Repository của chúng ta 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ì cách tiếp cận này, chúng tôi có thể cải thiện mã của bạn bằng cách tùy thuộc vào một giao diện độc lập đơn giản của nhà cung cấp:

<?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;
    }
}

Bây giờ, code không được kết hợp vớt bất kỳ vendor cụ thể nào, thậm chí cả Laravel. Kể từ khi contracts 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 của bạn mà không phải thay đổi mã nguồn của đoạn sử dụng cache nữa.

Sự đơn giản

Khi mà tất cả services của Laravel đều được định nghĩa trong các interface đơn giản, nó rất dễ dàng để xác định các chức năng được cung cấp bởi một service nhất định. Contract được sử dụng như một bản tài liệu ngắn gọn cho các tính năng của framework.

Thêm vào đó, khi bạn phụ thuộc vào các interface đơn giản, code 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ó sẵn trong một class lớn và phức tạp, bạn có thể tham khảo một interface đơn giản và clean.

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

Vậy thì làm thế nào để bạn có được một implementation của contract? Nó thực sự rất đơn giản.

Nhiều kiểu class trong Laravel được resolve qua các , bao gồm controllers, event listeners, middleware, queued jobs, và even route Closures. Vì thế, để có được một implementation của 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, kiểm tra .