Laravel Service Container

8/10/2021 DILaravel

# Description

  • Service Container là nơi quản lí và thực hiện các dependency của Laravel.
  • Để hiểu hơn về dependency, refer Dependency injection và Invertion of Control.

# Binding & Resolving

  • Binding: Register 1 dependency vào container.
  • Resolving: Lấy ra dependency trong container để sử dụng.

Với lập trình truyền thống, muốn sử dụng 1 class bạn phải chủ động khởi tạo class. Còn Laravel, các class đó đã được register tại service container, chỉ cần resolving để lấy ra sử dụng thôi. Đó là ý nghĩa của Invertion "đảo nghịch" trong IoC.

Các dependency mặc định của Laravel có thể xem tạo config/app.php

# Binding method:

Một vài method binfing cơ bản:

  • Singleton: Chỉ trả về 1 object instance dù resolved ở bất kì đâu. Thường sử dụng cho logger, configure,...

    use App\Services\Logger;
    
    $this->app->singleton(Logger::class, function () {
        return new Logger();
    })
        
    //
    $logger1 = App::make(Logger::class);
    $logger1->setOutputFile("logs/app.log");
    
    //
    $logger2 = App::make(Logger::class);
    echo $logger2->getOuputFile();
    // "logs/app.log"
       
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
  • Instance: Binding 1 object instance và resolved instance ở trong application.

    use App\Services\Transistor;
    use App\Services\PodcastParser;
    
    $service = new Transistor(new PodcastParser);
    
    $this->app->instance(Transistor::class, $service);
    
    //
    $transistor = App::make(Transistor::class);
    // Không cần set PodcastParser trong contructor của Transistor,
    // vì PodcastParser đã được inject vào contructor của Transistor tại container rồi
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
  • Interface: Binding interface cho implement. Linh hoạt khi muốn thay đổi lớp implement, maintain, test,..

    Có thể type hinting interface ở mội nơi trong application. Container sẽ tự động resolved implement đã được binding cho interface đó.

    "program to an interface, not an implementation"

    use App\Contracts\UserRepository;
    use App\Services\User;
    
    $this->app->bind(UserRepository::class, User::class);
    
    // ------
    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }
    
    //  $userRepository: hiện tại là 1 instance của User
    //  Muốn thay đổi lớp implement chỉ cần binding lại lớp implement khác trong container
    //   mà không cần thay đổi code ở Logic khi sử dụng interface UserRepository
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    • Contextual: Inject lớp implement khác nhau cho từng class
    $this->app->when(FooController::class)
              ->needs(Notification::class)
              ->give(function () {
                  return SlackNotification();
              });
    
    $this->app->when([Bar1Conttoller::class, Bar2Conttoller::class])
              ->needs(Notification::class)
              ->give(function () {
                  return MailkNotification();
              });
    
    // FooController container sẽ tự động inject SlackNotification cho interface Notification
    // Bar1Conttoller, Bar2Conttoller là MailkNotification
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14

# Auto Inject:

Laravel Container sẽ auto inject các dependency được type hinting trong application.

// extends example of UserRepository

// App\Services\User;

class User {
    protected $userModel;
    public function __construct(UserModel $userModel) {
        $this->userModel = $userModel;
    }
}

// UserController đang type-hint UserRepository()
// UserService đang type-hint UserModel() -- UserRepository đã được binding với UserService
1
2
3
4
5
6
7
8
9
10
11
12
13

Nếu không có container, khi sử dụng sẽ cần:

new UserController(new UserService(new UserModel()));
1

Laravel Container auto tìm các instance dependency và inject cho bạn. Nhờ sử dụng DI Container, ReflectionClass và đệ quy.


Xem thêm: DI Container (opens new window) Tạo DI Container (opens new window)