29/09/2018, 11:43

Service Container

Khi làm việc với Laravel thì không thể không nhắc đến Service Container một khái niệm cốt lõi trong Laravel mà bạn ko thể bỏ qua. Service Container thực sự là trái tim của framework này. Hôm nay tôi sẽ hướng dẫn các bạn cách mà service container hoạt động như thế nào. Khi bạn hiểu được nó thì bạn sẽ ...

Khi làm việc với Laravel thì không thể không nhắc đến Service Container một khái niệm cốt lõi trong Laravel mà bạn ko thể bỏ qua. Service Container thực sự là trái tim của framework này. Hôm nay tôi sẽ hướng dẫn các bạn cách mà service container hoạt động như thế nào. Khi bạn hiểu được nó thì bạn sẽ tự tin hơn khi làm việc với Laravel.

Nguyên văn của trang chủ laravel:

"The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are "injected" into the class via the constructor or, in some cases, "setter" methods."  Từng đó thôi có lẽ các bạn sẽ không hiểu hết về công cụ mạnh mẽ này. 

trước hết chúng ta sẽ nhắc đến 2 khái niệm mà liên quan đến Service Container.

Dependency Injection:

Nếu một Class A hoạt động phụ thuộc vào một vài Class B, thì thay vì khởi tạo các instance của Class B bên trong Class A, ta sẽ inject những instance của class B vào thông qua constructor hay setter. Instance của Class B mà Class A cần để hoạt động đó được gọi là dependency. 

ví dụ: 

<?php

class A {
	public function getName(){
		return 'Hello World';
	}
}

//ta sẽ khởi tạo instance của class A bên trong class B
class B
{
	protected $a;

	public function __construct(){
		$this->a = new A;
	}

	public function getName()
	{
		return $this->a->getName();
	}
}

$b = new B;

echo $b->getName(); // Hello world


// Dependency Injection: thay vì khởi tạo trược tiếp class A bên trong class C thì tao sẽ truyển thể hiện của class A vào class C thông qua constructor hoặc setter
// trong trường hợp dưới là constructor
class C {

	protected $a;
	
	public function __construct(A $a){
		$this->a = $a;
	}

	public function getName()
	{
		return $this->a->getName();
	}
}

$a = new A;
$c = new C($a);

Inversion of Control(IoC):

IoC là một kỹ thuật trong lập trình làm "đảo ngược" flow của những xử lý truyền thống. Bình thường thì đoạn xử lý logic của ta sẽ gọi đến những class, library mà nó cần dùng, thế nhưng, với IoC thì ta sẽ gửi cho những đoạn xử lý logic đó những thứ mà nó cần. Với IoC thì các sự phụ thuộc sẽ được ghép vào các đối tượng tương ứng tại thời điểm run time thay vì compile time.

<?php

// đăng kí với Container
App::bind('C', function($app){
	return new C(new A, new B);
});

// với IoC Container khi lấy ra 
$c = App::make('C');

// theo cách thông thường 
$c = new C(new A, new B);

Nhìn ở ví dụ trên thì bạn có thể nghĩ rằng cách 1 sẽ dài dòng hơn đúng không ? Nhưng bạn hãy suy nghĩ lại với trường hợp C được khởi tạo theo cách thông thường mà việc khởi tạo phức tạp hơn tốn nhiều công sức hơn, và bạn lại phải khởi tạo C ở nhiều nơi. Như vậy đoạn code để khởi tạo C sẽ lặp đi lặp lại nhiều lần. Đó là lý do tại sao IoC container lại có ý nghĩa vì logic khởi tạo C chỉ được gói gọn tạo một nơi và sau đó ta chỉ việc gọi nó. Như trong ví dụ trên thì cụ thể là $c = App::make('C') đơn giản vậy thôi.

Đến đây các bạn đã hiểu về 2 khái niêm trên. Tiếp theo mình muốn giới thiệu các tính năng mạnh mẽ mà Service Container (IoC) mang lại cho các bạn.
 

Binding:

Hầu như tất cả các Service Container binding của bạn sẽ được đăng ký trong các Service Provider, vì vậy hầu hết các ví dụ này sẽ chứng minh bằng cách sử dụng Container trong Service Provider

Simple Bindings: 

Trong các serivce provider, bạn luôn có quyền truy cập vào container thông qua thuộc tính $this->app. Chúng ta có thể đăng ký một binding bằng cách sử dụng phương thức bind, truyền tên lớp hoặc tên giao diện mà chúng ta muốn đăng ký cùng với một Closure để trả về một cá thể của lớp:

$this->app->bind('HelpSpot\API', function ($app) {
    return new HelpSpot\API($app->make('HttpClient'));
});




 

Hoa Nguyen viết 10:07 ngày 06/10/2018

Binding A Singleton
 

$this->app->singleton('HelpSpot\API', function ($app) { return new HelpSpot\API($app->make('HttpClient')); });

 

Binding Instance
 

$api = new HelpSpot\API(new HttpClient);

$this->app->instance('HelpSpot\API', $api);


Binding Interfaces To Implementations
 

$this->app->bind( 'App\Contracts\EventPusher', 'App\Services\RedisEventPusher' );

 

Contextual Binding

Sometimes you may have two classes that utilize the same interface, but you wish to inject different implementations into each class. For example, two controllers may depend on different implementations of the Illuminate\Contracts\Filesystem\Filesystem contract. Laravel provides a simple, fluent interface for defining this behavior:

use Illuminate\Support\Facades\Storage;
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;

$this->app->when(PhotoController::class)
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('local');
          });

$this->app->when([VideoController::class, UploadController::class])
          ->needs(Filesystem::class)
          ->give(function () {
              return Storage::disk('s3');
          });
+3