Bài này khá dài nên đánh mục lục cho tiện theo dõi nhé các bạn

I. Zero Configuration Resolution
II. Binding

  1. Basic Binding
    1.1 Simple Binding
    1.2 Binding a Singleton
    1.3 Binding Scoped Singletons
    1.4 Binding Instances
  2. Binding Interfaces to Implementations
  3. Contextual Binding
  4. Binding Primitives
  5. Binding Typed Variadics
    5.1 Variadic Tag Dependencies
  6. Tagging
  7. Extending Bindings

III. Resolving


Oke, let’s go.

Bài này chúng ta sẽ tìm hiểu xem Laravel xử lý các dependences như thế nào nhé.
Xem ví dụ giới thiệu của Laravel dưới đây

<?php
 
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Repositories\UserRepository;
use App\Models\User;
use Illuminate\View\View;
 
class UserController extends Controller
{
    /**
     * Create a new controller instance.
     */
    public function __construct(
        protected UserRepository $users,
    ) {}
 
    /**
     * Show the profile for the given user.
     */
    public function show(string $id): View
    {
        $user = $this->users->find($id);
 
        return view('user.profile', ['user' => $user]);
    }
}

Hiểu đơn giàn là UserController đang inject UserRepository vào hàm __construct(), và dùng nó để truy xuất thông tin của user

$user = $this->users->find($id);

Như vậy, chẳng cần quan tâm UserRepository – bản thân nó có những sự phụ thuộc nào, nếu muốn sử dụng, chỉ cần inject vào constructor của UserController và sử dụng thôi. Vì đơn giản UserRepository đã được quản lý bởi Service Container – Một công cụ tối quan trọng, phổ biến trong Laravel.

Tiếp theo, trước khi tìm hiểu làm thế nào Laravel làm điều đó như thế nào, chúng ta sẽ hiểu về “Zero Configuration Resolution”

I. Zero Configuration Resolution

Google dịch “Độ phân giải cầu hình bằng 0” :D. Hiểu nôm na là các bạn có thể inject các dependences mà không cần phải khai báo hay cấu hình gì cả.

Theo Laravel: bạn có thể inject tùy ý các classes mà không cần phải khai báo/cấu hình, các classes này phải thuộc dang không có sự phụ thuộc vào các classes khác, hoặc chỉ phụ thuộc vào các classes concrete (tức là class không phải interface)
Xem ví dụ sau:

class Service
{
    // đây là một class không có phụ thuộc, hoặc chỉ phụ thuộc concrete classes.
}

// Nên không cần phải cấu hình, các bạn có thể inject một cách tự nhiên khi cần.
Route::get('/', function (Service $service) {
    die($service::class);
});

Như vậy, khi inject các classes interface hoặc classes có sự phụ thuộc vào interface thì các bạn sẽ cần phải khai báo / cấu hình cho Service Container biết. Trong Laravel, để làm điều này, chúng ta sẽ làm quen với khái niệm “Binding”

II. Binding

Binding là khái niệm chỉ sự đăng ký sự phụ thuộc.

Trong phần này chúng ta sẽ lần lượt làm quen với

  • Basic Binding
    – Simple Binding
    – Binding a singleton
    – Binding Scoped Singletons
    – Binding Instances
  • Binding Interfaces to Implementations
  • Contextual Binding
  • Binding Primitives
  • Binding Typed Variadics
    – Variadic Tag Dependencies
  • Tagging
  • Extending Bindings

Nói chung khá là dài nhưng đừng lo, chúng ta sẽ đi một cách chi tiết và từ tốn nhé =)).

Đầu tiên hãy làm quen với các Basic Binding nhé.

1. Basic Binding

1.1 Simple Binding

– Các bạn có thể làm điều này trong Service Providers (Chúng ta sẽ tìm hiểu cụ thể Service Providers là gì sau nhé, giờ cứ hiểu nôm na nó là một phần rất quan trọng khi lifecycle chuyển hướng từ Kernel tới Service Providers, rồi tới router…)
Để truy xuất tới Service Contrainer (Từ giờ gọi tắt là Container cho nhanh nhé) trong Service Providers thì dùng $this->app nhé
Ví dụ:

use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;

/*
* Hàm bind dưới đây gồm 2 tham số
* Tham số đầu tiên là Transistor::class, điều này chỉ ra rằng bạn đang đăng ký class này vào Container
* Tham số thứ 2 là một Closure function
* Hàm này trả về instace của Transitor
* Tham số truyền vào là $app->make(PodcastParser::class)
* Thực ra đây là cách Container resolve (nôm na là gọi ra) một class đã được đăng ký trong Container trước đó
*/
$this->app->bind(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

– Các bạn cũng có thể binding ngoài Service Providers, để làm điều này thì sử dụng App Thay vì
$this->app nhé

Ví dụ:

use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Support\Facades\App;
 
App::bind(Transistor::class, function (Application $app) {
    // ...
});

1.2 Binding a singleton

Cách bind như sau

use App\Services\Transistor;
use App\Services\PodcastParser;
use Illuminate\Contracts\Foundation\Application;
 
$this->app->singleton(Transistor::class, function (Application $app) {
    return new Transistor($app->make(PodcastParser::class));
});

Đơn giản là chỉ dùng hàm singleton thay vì hàm bind thôi.

Khi binding với singleton, thì trong mọi lần gọi tới Container, các instance object sẽ là như nhau. Nó khác với simple binding, simple binding sẽ luôn tạo instance object mới mỗi khi gọi tới Container.
Xem ví dụ sau sẽ rõ hơn nhé

Ta có một class đơn giản

<?php

namespace App\Support;

class TestClass
{
    protected $value = 0;

    public function increase()
    {
        // Tăng giá trị lên 1 đơn vị
        $this->value++;

        return $this->value;
    }
}

Ta dùng Bind Simple

// test1 là tên tùy đặt, sau này gọi instance ra thì các bạn dùng test1
$this->app->bind(
    'test1',
    \App\Support\TestClass::class
);

Giờ mình create instance (Từ bây giờ gọi là resolve cho nhanh nhé) và gọi tới hàm increase

app('test1')->increase() // trả về giá trị là 1
app('test1')->increase() // trả về giá trị là 1
app('test1')->increase() // trả về giá trị là 1

Như vậy, giá trị trả về luôn là 1, do Simple Bind luôn luôn “tạo mới” (giờ gọi là constructed cho nhanh nhé).

Giờ thử dùng Bind a Singleton

$this->app->singleton(
    'test2',
    \App\Support\TestClass::class
);

Tiếp theo thử resolve

app('test2')->increase() // trả về giá trị là 1
app('test2')->increase() // trả về giá trị là 2
app('test2')->increase() // trả về giá trị là 3

Có nghĩa là object chỉ được constructed một lần duy nhất, các lần resolve sau giữ lại object instance đó cũng như giữ lại trạng thái trong suốt quá trình thực hiện.

Oke, bài này tạm thời dừng lại ở đây thôi, tránh tẩu hỏa nhập ma.

Hẹn các bạn ở bài sau, chúng ta sẽ đi tiếp các phần còn lại của Basic binding 😉

By HNK

Leave a Reply

Your email address will not be published. Required fields are marked *