Service container là gì ?

Laravel service container là một công cụ để quản lý các depedencies class và thực hiện việc xử lý các depedencies injection. Các dependencies class thường được inject vào các class khác thông qua _contruct() hay setter.

  • Ví dụ : 
namespace App\Http\Controllers;
use App\User;
use App\Repositories\UserRepository;
use App\Http\Controllers\Controller;
class UserController extends Controller
{
/**
* The user repository implementation.
*
* @var UserRepository
*/
protected $users;
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the profile for the given user.
*
* @param int $id
* @return Response
*/
public function show($id)
{
$user = $this->users->find($id);
return view('user.profile', ['user' => $user]);
}
}

 

Service container được coi như là trái tim của laravel việc hiểu được về cơ chế này vô cùng quan trọng.

 

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

 

Gần như tất cả các service container binding đều được khai báo trong các service providers. Không cần phải bind class vào trong container nếu nó không phụ thuộc vào bất kỳ một class nào khác. Container đơn giản chỉ giúp đỡ chúng ta khởi tạo các class phụ thuộc vào class khác một cách dễ dàng và đúng đắn. Chúng ta cùng mở tập tin HashServiceProvider trong core của laravel. .

 

<?php
namespace Illuminate\Hashing;
use Illuminate\Support\ServiceProvider;
class HashServiceProvider extends ServiceProvider
{
/**
* Indicates if loading of the provider is deferred.
*
* @var bool
*/
protected $defer = true;
/**
* Register the service provider.
*
* @return void
*/
public function register()
{
$this->app->singleton('hash', function () {
return new BcryptHasher;
});
}
/**
* Get the services provided by the provider.
*
* @return array
*/
public function provides()
{
return ['hash'];
}
}

 

Tại đây chúng ta bind một đối tượng của "BcryptHasher" vào trong từ khóa hash thông qua một hàm ẩn danh. Bên trong một service provider chúng ta luôn có thể truy cập vào trong container thông qua thuộc tính $this->app. Chúng ta có thể bind một đối tượng hay một interface vào một từ khóa và sau đó lấy ra chúng dễ dàng tại controller hay bất cứ 1 nơi nào khác trong ứng dụng. Chúng ta có thể hiểu đơn giản service container giống như một cái tủ khóa rất to trong siêu thị khi bạn đi vào siêu thị sẽ phải gửi
đồ. Và quá trình bind này chính là quá trình gửi đồ đó. Sau khi bạn đã gửi đồ bạn sẽ có 1 cái chìa khóa. Chìa khóa đó ở đây chính là từ khóa mà bạn đã bind vào trong container. Sau này khi bạn muốn lấy đồ ra bạn cần phải sử dụng đúng khóa để lấy đồ. Ở đây từ khóa hash chính là chìa khóa. Sau khi bind chúng ta hoàn toàn có thể lấy được từ trong container thông qua quá trình resolve các dependecies class của đối tượng đang được thực hiện. Binding a singleton. Phương thức singleton thực hiện liên kết một class hay một interface vào trong container mà chỉ cần thực hiện duy nhất 1 lần, sau đó tại bất cứ đâu khi bạn lấy ra đối tượng đó cũng chỉ được khởi tạo duy nhất một lần mà thôi.

 

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

Binding instances
Bạn cũng có thể bind một đối tượng có sẵn vào trong container bằng việc sử dụng
phương thức instance() . Instance này sẽ luôn được trả về khi bạn truy cập vào
container

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

Binding primities
Thỉnh thoảng bạn muốn có một class mà nhận một vài class injection. Nhưng cũng cần một biến truyền vào trong quá trình inject bạn có thể sử dụng cách sau :

$this->app->when('App\Http\Controllers\UserController')
->needs('$variableName')
->give($value);

Binding interface tới implementions
Một tính năng rất manh mẽ của service container là nó có khả năng bind một interface tới một implemention của nó. Ví dụ giả sử rằng chúng ta có một interface EventPusher và một RedisEventPusher.

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

Câu lệnh bên trên sẽ nói cho container biết phải inject RedisEventPusher khi một
class cần implementions của EventPusher. Bây giờ chúng ta có thể type hint
eventPusher interface vào trong constructor của 1 class :

use App\Contracts\EventPusher;
/**
* Create a new class instance.
*
* @param EventPusher $pusher
* @return void
*/
public function __construct(EventPusher $pusher)
{
$this->pusher = $pusher;
}

Contextual binding :

Khi bạn có 2 implemetation trở lên của 1 interface bạn có thể dùng cách dưới đây để chỉ định implementation của interface nào sẽ được inject theo từng trường hợp :

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)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});

Resolving từ container :
Sử dụng từ khóa make : Bạn có thể sử dụng từ khóa make để lấy đối tượng của một class ra khỏi container. Phương thức này chấp nhận tên của một class hay tên một interface mà bạn muốn
lấy ra :

$api = $this->app->make('HelpSpot\API');

Nếu bạn ở trong ứng dụng tại một nơi không thể truy cập vào biến app bạn có thể sử dụng function helper resolve để lấy đối tượng từ trong container :

$api = resolve('HelpSpot\API');

Nếu một vài class không thể resolve ra khỏi container bạn phải inject chúng bằng việc truyền cho chúng một mảng kết hợp như trường hợp dưới đây vào trong method makeWidth :

$api = $this->app->makeWith('HelpSpot\API', ['id' => 1]);

Tự động injection :
Laravel sẽ tự động type hint dependency vào trong các controller , event listinger , queue jobs, middleware ... Bạn chỉ cần type hint vào trong các constructor và laravel sẽ tự động injection giúp bạn.

namespace App\Http\Controllers;
use App\Users\Repository as UserRepository;
class UserController extends Controller
{
/**
* The user repository instance.
*/
protected $users;
/**
* Create a new controller instance.
*
* @param UserRepository $users
* @return void
*/
public function __construct(UserRepository $users)
{
$this->users = $users;
}
/**
* Show the user with the given ID.
*
* @param int $id
* @return Response
*/
public function show($id)
{
//
}
}

Khóa học Lập trình PHP 7 Laravel Framework 5.5 giảng dậy theo dự án thực tế khai giảng ngày 22/6