I. Zero Configuration Resolution
II. Binding
- Basic Binding
1.1 Simple Binding
1.2 Binding a Singleton
1.3 Binding Scoped Singletons
1.4 Binding Instances - Binding Interfaces to Implementations
- Contextual Binding
- Binding Primitives
- Binding Typed Variadics
5.1 Variadic Tag Dependencies - Tagging
- Extending Bindings
III. Resolving
Chúng ta đã đi qua Basic Binding, một kiến thức cơ bản nhưng cực kỳ quan trọng. Giờ chúng a sẽ tìm hiểu tiếp các Binding còn lại nhé. Hơi dài, chịu hó đọc từ từ nhé các bợn =))
2. Binding Interfaces to Implementations
Gỉa sử bạn có một interface, gọi là EventPusher. Và bạn có một implementation (tức là một class implement interface kia), gọi là RedisEventPusher
Bây giờ nếu các bạn muốn inject interface EventPusher vào một class nào đó, dĩ nhiên các bạn phải bind interface này vào Container. Và ở đây, khi bind vào Container các bạn sẽ chỉ ra class đã implement nó
Cách dùng:
use App\Contracts\EventPusher;
use App\Services\RedisEventPusher;
$this->app->bind(EventPusher::class, RedisEventPusher::class);3. Contextual Binding
Sau khi đọc về binding interfaces ở trên, chắc bạn sẽ thắc mắc: Nếu có nhiều implementation thì sao ? Vâng, Contextual Binding giúp ta giải quyết vấn đề này
Xem ví dụ dưới đây
use App\Http\Controllers\PhotoController;
use App\Http\Controllers\UploadController;
use App\Http\Controllers\VideoController;
use Illuminate\Contracts\Filesystem\Filesystem;
use Illuminate\Support\Facades\Storage;
// Filesystem là một interface
// Storage::disk('local'), Storage::disk('s3') hiểu đơn giản là 2 implementions
// Đối với controller PhotoController thì bind implementaion Storage::disk('local')
$this->app->when(PhotoController::class)
->needs(Filesystem::class)
->give(function () {
return Storage::disk('local');
});
// Đối với 2 controllers VideoController, UploadController thì bind implementation Storage::disk('s3')
$this->app->when([VideoController::class, UploadController::class])
->needs(Filesystem::class)
->give(function () {
return Storage::disk('s3');
});4. Binding Primitives
Primitives (Dịch ra là nguyên thủy) 🙂 Hiểu đơn giản, các bạn muốn khởi tạo một giá trị mặc định cho một variable trong một class, các bạn có thể làm như sau
đây là class ví dụ
class UserController
{
protected $variableName;
__construct($variableName) {}
}Giờ muốn khởi tạo giá trị mặc định thì làm như sau
use App\Http\Controllers\UserController;
$this->app->when(UserController::class)
->needs('$variableName')
->give('hello world');Như vậy khi classs đc gọi tới thì giá trị mặc định của $variableName sẽ là “hello world”
Thực ra Primitives cũng thuộc về khái niệm Contextual. Binding Contextual hiểu rộng ra thì nó không chỉ giải quyết bài toán interface có nhiều implemetation, mà nó còn giúp define giá trị mặc định của các variables.
Không những thế, Contextual còn giúp xử lý các dạng dependences có dạng Array, như ở phần 5 dưới đây.
5. Binding Typed Variadics
Xem class dưới đây
<?php
use App\Models\Filter;
use App\Services\Logger;
class Firewall
{
/**
* The filter instances.
*
* @var array
*/
protected $filters;
/**
* Create a new class instance.
*/
public function __construct(
protected Logger $logger,
Filter ...$filters,
) {
$this->filters = $filters;
}
}class Firewall có inject một mảng các instance của Filter. Để có thể inject dependence này vào thì làm như sau
$this->app->when(Firewall::class)
->needs(Filter::class)
->give(function (Application $app) {
return [
$app->make(NullFilter::class),
$app->make(ProfanityFilter::class),
$app->make(TooLongFilter::class),
];
});Hoặc để cho ngắn gọn thì viết như sau
$this->app->when(Firewall::class)
->needs(Filter::class)
->give([
NullFilter::class,
ProfanityFilter::class,
TooLongFilter::class,
]);Phần 5.1 Variadic Tag Dependencies mình sẽ nói sau, khi đã đi qua phần 6. Tagging
6. Tagging
Gỉa sử các bạn có rất nhiều loại reports (CpuReport, MemoryReport, …). Các report này là implementations của report interface. Giờ chúng ta nhóm cá reports này lại sau khi đã binding
$this->app->bind(CpuReport::class, function () {
// ...
});
$this->app->bind(MemoryReport::class, function () {
// ...
});
$this->app->tag([CpuReport::class, MemoryReport::class], 'reports');Giờ muốn lấy các dependences này thì đơn giản là gọi $app->tagged(‘reports’)
$this->app->bind(ReportAnalyzer::class, function (Application $app) {
return new ReportAnalyzer($app->tagged('reports'));
});Quay trở lại phần 5.1, giả sử class của chúng ta có thểm Reports …$reports (tức là một mảng các report). Chúng ta làm như sau để inject chúng vào
$this->app->when(ReportAggregator::class)
->needs(Report::class)
->giveTagged('reports');7. Extending Bindings
Tham khảo ví dụ: https://viblo.asia/p/tap-6-service-container-laravel-RQqKL2drl7z
Gỉa sử bạn có một Service đã được resolved. Ví dụ như: app/User.php
public function test()
{
return "test() method of User class";
}Nếu muốn override lại function test, chúng ta tạo thêm một class app/DecoratedUser.php
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function decoratedTest()
{
return 'Decorated: ' . $this->user->test();
}
Sau đó thì extend nó như sau:
// Biến $user trong closure function dùng là thể hiện cho class App\User
$this->app->extend('App\User', function($user) {
return new \App\DecoratedUser($user);
});Khi resolve như sau
Route::get('/', function() {
return app()->make('App\User')->decoratedTest();
});Các bạn có thể thấy, mặc dù đang resolve App\User nhưng chúng ta có thể gọi tới function decoratedTest của app/DecoratedUser.php, đó là do chúng ta đã binding extending ở phía trên.
Cuối cùng cũng tới phần cuối
III. Resolving
Để resolve một class từ Container thì làm như sau
use App\Services\Transistor;
// Dùng $this->app->make
$transistor = $this->app->make(Transistor::class);Hoặc
use App\Services\Transistor;
use Illuminate\Support\Facades\App;
// Sử dụng App facade
$transistor = App::make(Transistor::class);
// Hoặc sử dụng app() helper
$transistor = app(Transistor::class);Khi muốn resolve class cùng với tham số mặc định
use App\Services\Transistor;
$transistor = $this->app->makeWith(Transistor::class, ['id' => 1]);Trong “controller”, “middleware”, “event listener”… Laravel cung cấp cơ chế tự động resolve
(gọi là Automatic Injection)
Xem ví dụ
<?php
namespace App\Http\Controllers;
use App\Repositories\UserRepository;
use App\Models\User;
class UserController extends Controller
{
/**
* Create a new controller instance.
*/
public function __construct(
protected UserRepository $users,
) {}
/**
* Show the user with the given ID.
*/
public function show(string $id): User
{
$user = $this->users->findOrFail($id);
return $user;
}
}Các bạn thấy UserRepository sẽ được tự động resolve khi các bạn khai báo vào __construct function.
Và thi thoảng các bạn muốn vừa resolve class, vừa gọi tới một function của nó.
Ví dụ các bạn có class sau
<?php
namespace App;
use App\Repositories\UserRepository;
class UserReport
{
/**
* Generate a new user report.
*/
public function generate(UserRepository $repository): array
{
return [
// ...
];
}
}Bây giờ vừa resolve class trên vừa gọi tới function generate thì làm như sau
use App\UserReport;
use Illuminate\Support\Facades\App;
$report = App::call([new UserReport, 'generate']);Cuối cùng, nếu bạn muốn “gửi đi một sự kiên (event)” khi class được resolve thì làm như sau
use App\Services\Transistor;
use Illuminate\Contracts\Foundation\Application;
$this->app->resolving(Transistor::class, function (Transistor $transistor, Application $app) {
// Khi resolve class Transistor thì xử lý đoạn code này ...
});
$this->app->resolving(function (mixed $object, Application $app) {
// Khi resolve bất khi bất kì classes nào thì xử lý đoạn code này ...
});Oke! Vậy là cũng tương đối đầy đủ về Serivce Container. Khi đọc hết 4 phần, tôi nghĩ các bạn đã hiểu “lờ mờ” nó là cái gì. Để hiểu hơn thì chúng ta phải nghiền ngẫm lại, tìm hiểu thêm, đặc biệt là phải va chạm các bài toán thực tế.
Anyway, không sao cả. Cứ keep in mind là với giai đoạn hiện tại, chúng ta chỉ cần hiểu lý thuyết ở mức “tương đối” như vậy thôi. Tôi sẽ cùng các bạn tiếp tục khám phá trong các bài tiếp theo, mọi thứ sẽ dần dần được sáng tỏ hơn.
Chúc các bạn thành công và giữ vững động lực học tập 🙂