Laravel 8: Tìm hiểu về Service Container (phần 1)

Phần 2 Bài viết này sẽ giới thiệu với các bạn về Laravel Service Container và cách chúng inject, resolve. Giới thiệu Laravel service container là một công cụ mạnh mẽ để quản lý các dependencies của lớp và thực hiện dependency injection. Dependency injection là một cụm từ đặc biệt, về cơ bản có

Phần 2

Bài viết này sẽ giới thiệu với các bạn về Laravel Service Container và cách chúng inject, resolve.

Giới thiệu

Laravel service container là một công cụ mạnh mẽ để quản lý các dependencies của lớp và thực hiện dependency injection. Dependency injection là một cụm từ đặc biệt, về cơ bản có nghĩa là: các dependencies được “đưa” vào lớp thông qua hàm constructor hoặc trong một số trường hợp là các phương thức “setter”.

Hiểu biết sâu sắc về Laravel service container là điều cần thiết để xây dựng một ứng dụng lớn, mạnh mẽ, cũng như để contribute Laravel core.

Dưới đây là một ví dụ về Service Container:

<?phpnamespaceAppHttpControllers;useAppHttpControllersController;useAppRepositoriesUserRepository;useAppModelsUser;classUserControllerextendsController{/**
     * The user repository implementation.
     *
     * @var UserRepository
     */protected$users;/**
     * Create a new controller instance.
     *
     * @param  UserRepository  $users
     * @return void
     */publicfunction__construct(UserRepository$users){$this->users=$users;}/**
     * Show the profile for the given user.
     *
     * @param  int  $id
     * @return Response
     */publicfunctionshow($id){$user=$this->users->find($id);returnview('user.profile',['user'=>$user]);}}

Lớp UserController cần truy xuất user từ database. Bạn sẽ “đưa” vào lớp một service có khả năng thực hiện công việc này. Cụ thể trong ví dụ trên, lớp UserRepository sử dụng Eloquent để lấy thông tin user từ database.

Tự động giải quyết cấu hình

Nếu một lớp không có dependencies hoặc chỉ phụ thuộc vào các lớp cụ thể khác (không phải interface), container không cần được hướng dẫn về cách giải quyết lớp đó. Ví dụ: bạn có thể đặt code sau trong file route / web.php của mình:

<?phpclassService{//}Route::get('/',function(Service$service){die(get_class($service));});

Trong ví dụ này, khi gọi đến route / của ứng dụng sẽ tự động giải quyết lớp Service và đưa nó vào route handler của bạn. Do đó, bạn có thể tận dụng lợi thế của việc sử dụng dependency mà không phải lo lắng về các file cấu hình bị cồng kềnh.

Nhiều lớp bạn viết khi xây dựng ứng dụng Laravel sẽ tự động nhận các dependencies của chúng thông qua container, bao gồm controllers, event listeners, middleware,… Ngoài ra, bạn có thể nhập gợi ý dependecy trong phương thức handle của các công việc được xếp hàng đợi. Một khi bạn đã trải nghiệm sức mạnh cấu hình tự động của dependencies injection, bạn sẽ cảm thấy không thể phát triển ứng dụng nếu không có nó.

Khi nào sử dụng Container?

Do không cần tự quyết định cấu hình, bạn thường sẽ nhập gợi ý các dependencies vào routes, controllers, event listeners và các nơi khác mà không cần tương tác thủ công với container.

Ví dụ: bạn có thể nhập gợi ý đối tượng Illuminate Http Request trong định nghĩa route để có thể dễ dàng truy cập request hiện tại.

use IlluminateHttpRequest;

Route::get('/', function (Request $request) {
    // ...
});

Trong nhiều trường hợp, nhờ tính năng chèn dependencies tự động và facades, bạn có thể xây dựng các ứng dụng Laravel mà không cần tự binding hoặc giải quyết bất cứ thứ gì từ container. Vậy khi nào bạn sẽ cần tương tác thủ công với container? Hãy xem xét hai tình huống.

  1. Nếu bạn viết một lớp implements một interface và bạn muốn nhập gợi ý interface đó trong một route hoặc phương thức khởi tạo lớp, bạn phải cho container biết cách giải quyết interface đó.
  2. Nếu bạn đang viết một Laravel package mà bạn định chia sẻ với các developers Laravel khác, bạn có thể cần phải bind các services của package vào container.

Resolving

Trong một service provider, bạn luôn có quyền truy cập vào container thông qua thuộc tính$this->app. Nếu bạn muốn tương tác với container bên ngoài một service provider, bạn có thể thông qua facade.

make()

Bạn có thể sử dụng phương thức make để giải quyết một instance từ container. Phương thức make nhận tên của lớp hoặc interface mà bạn muốn giải quyết:

useAppServicesTransistor;$transistor=$this->app->make(Transistor::class);

Nếu một số dependencies không thể giải quyết được thông qua container, bạn có thể inject chúng vào bằng cách chuyển chúng sang dạng một mảng liên kết rồi truyền vào phương thức makeWith. Ví dụ: bạn có thể truyền thủ công tham số hàm khởi tạo $id theo yêu cầu của service Transition:

useAppServicesTransistor;$transistor=$this->app->makeWith(Transistor::class,['id'=>1]);

Nếu bạn đang ở bên ngoài service provider và bạn không có quyền truy cập vào biến $app, bạn có thể sử dụng App facade để giải quyết một instance từ container:

useAppServicesTransistor;useIlluminateSupportFacadesApp;$transistor=App::make(Transistor::class);

Nếu bạn muốn container instance tự inject vào một lớp đang được container giải quyết, bạn có thể type-hint lớp Illuminate Container Container trong phương thức khởi tạo của lớp:
use IlluminateContainerContainer;

/**
 * Create a new class instance.
 *
 * @param  IlluminateContainerContainer  $container
 * @return void
 */publicfunction__construct(Container$container){$this->container=$container;}

Automatic Injection

Bạn có thể type-hint dependency trong phương thức khởi tạo của một lớp được container giải quyết, bao gồm controllers, event listeners, middleware,… Ngoài ra, bạn có thể type-hint dependency trong phương thức handle của queued jobs. Trong thực tế, đây là cách hầu hết các đối tượng sẽ được giải quyết bởi container.

Ví dụ: bạn có thể type-hint một repository trong phương thức khởi tạo của controller. Repository sẽ tự động được resolve và inject vào lớp:

<?phpnamespaceAppHttpControllers;useAppRepositoriesUserRepository;classUserControllerextendsController{/**
     * The user repository instance.
     *
     * @var AppRepositoriesUserRepository
     */protected$users;/**
     * Create a new controller instance.
     *
     * @param  AppRepositoriesUserRepository  $users
     * @return void
     */publicfunction__construct(UserRepository$users){$this->users=$users;}/**
     * Show the user with the given ID.
     *
     * @param  int  $id
     * @return IlluminateHttpResponse
     */publicfunctionshow($id){//}}

Method Invocation & Injection

Đôi khi bạn muốn gọi một phương thức trên một instance trong khi vẫn cho phép container tự động inject các dependencies của phương thức đó. Ví dụ, cho lớp sau:

<?phpnamespaceApp;useAppRepositoriesUserRepository;classUserReport{/**
    * Generate a new user report.
    *
    * @param  AppRepositoriesUserRepository  $repository
    * @return array
    */publicfunctiongenerate(UserRepository$repository){// ...}}

Bạn gọi phương thức generate thông qua container như sau:

useAppUserReport;useIlluminateSupportFacadesApp;$report=App::call([newUserReport,'generate']);

Phương thức call của container được dùng để gọi một closure trong khi đang tự động inject các dependencies của nó
The call method accepts any PHP callable. The container’s call method may even be used to invoke a closure while automatically injecting its dependencies:

useAppRepositoriesUserRepository;useIlluminateSupportFacadesApp;$result=App::call(function(UserRepository$repository){// ...});

Container Events

Service container kích hoạt một sự kiện mỗi khi nó giải quyết một đối tượng. Bạn có thể lắng nghe sự kiện này bằng cách sử dụng phương thức “`resolving“:

useAppServicesTransistor;$this->app->resolving(Transistor::class,function($transistor,$app){// Called when container resolves objects of type "Transistor"...});$this->app->resolving(function($object,$app){// Called when container resolves object of any type...});

Như bạn có thể thấy, đối tượng đang được giải quyết sẽ được truyền vào callback, cho phép bạn bổ sung thuộc tính của đối tượng trước khi nó được gửi tới thành phần sử dụng nó.

PSR-11

Laravel service container implements interface PSR-11. Do đó, bạn có thể type-hint container interface PSR-11 để có một instance của Laravel container:

useAppServicesTransistor;usePsrContainerContainerInterface;Route::get('/',function(ContainerInterface$container){$service=$container->get(Transistor::class);//});

Một ngoại lệ được sinh ra nếu không thể giải quyết được identifier đã cho. Nếu identifier không bao giờ bị ràng buộc, ngoại lệ sẽ là instance của Psr Container NotFoundExceptionInterface. Nếu identifier đã bị ràng buộc nhưng không thể được giải quyết, ngoại lệ sẽ là instance của Psr Container ContainerExceptionInterface.

Tham khảo

https://laravel.com/docs/8.x/container

Phần 2

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ