Tìm kiếm bài viết

Laravel: Tìm hiểu về Service Container

12.10.2020

0/5 (0 Reviews)

Giới thiệu về Service Container trong laravel

    Giới thiệu về Service Container trong laravel

    Laravel service container là một công cụ rất mạnh trong việc quản lý các class dependencies và thực hiện xử lý dependency injection. Dependency injection là một cụm từ thể hiện có nghĩa là: các dependencies của class được "injected" vào trong class thông qua hàm khởi tạo hoặc trong một số trường hợp là các phương thức "setter". Hãy xem ví dụ đơn giản dưới đây:

    <?php
    
    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]);
        }
    }
    
    

    Trong ví dụ trên, UserController cần lấy thông tin người dùng từ một nguồn dữ liệu. Vì vậy, chúng ta sẽ inject vào một service có thể truy suất người dùng. Trong hoàn cảnh này, UserRepository có thể sử dụng Eloquent để lấy lại thông tin người dùng từ cơ sở dữ liệu. Tuy nhiên, khi repository được inject, chúng ta có thể dễ dàng trao đổi chúng với implementation khác. Chúng ta cũng dễ dàng "mock", hoặc tạo một dummy implementation của UserRepository khi testing ứng dụng của bạn.

    Sự hiểu biết sâu về Laravel service container là rất cần thiết cho việc phát triển ứng dụng mạnh mẽ,lớn và đóng góp cho core của Laravel.

    Binding

    Binding Basics

    Hầu như tất cả các service container binding của bạn sẽ được đăng Ký trong , vì vậy, hầu hết những ví dụ này sẽ minh hoạ cách sử dụng container trong bối cảnh đó.

     

    Simple Bindings

     

    Bên trong một service provider, bạn luôn luôn có quyền truy cập vào trong container thông qua thuộc tính $this->app. Chúng ta có thể đăng kí liên kết sử dụng phương thức bind, và truyền vào tên của class hay interface mà chúng ta muốn đăng kí cùng với Closure thực hiện trả về instance của class đó:

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

    Lưu ý rằng chúng ta nhận được container như một đối số truyền vào cho resolver. Sau đó thì chúng ta có thể thực hiện resolve các sub-dependencies con của đối tượng mà đang được xây dựng.

     

    Binding A Singleton

     

    Phương thức singleton thực hiện liên kết một class hoặc interface vào container mà chỉ cần thực hiện duy nhất một lần. Khi một singleton binding được giải quyết, cùng một object instance sẽ được trả về trong các subsequent calls vào container:

    $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 instance đang tồn tại vào trong container bằng cách sử dụng phương thức instance. Instance đó sẽ luôn luôn được trả về trong các lần gọi sau vào container:

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

     

    Binding Primitives

     

    Thỉnh thoảng bạn có một class nhật một vài injected class khác, nhưng cũng cần một inject giá trị nguyên thủy như một số nguyên. Bạn có thể dễ dàng sử dụng binding để inject bất kỳ giá trị nào vào trong class nếu cần:

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

    Binding Interfaces To Implementations

     

    Một tính năng tuyệt vởi của service container là nó có khả năng bind một interface thành một implementation. Ví dụ, giả sử chúng ta có interface EventPusher và một implementation RedisEventPusher. Khi đã có code của implementation RedisEventPusher cho interface, chúng ta có thể đăng ký nó với service container như sau:

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

    Câu lệnh này sẽ nói với container nó sẽ inject RedisEventPusher khi một class nào đó cần một implementations từ interface EventPusher. Chúng ta có thể type-hint interface EventPusher interface trong một constructor, hay bất cứ vị trí nào mà dependencies có thể được inject bởi service container:

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

    Contextual Binding

     

    Đôi khi bạn sẽ có hai class triển khai từ cùng một interface nhưng bạn muốn inject các implementations khác nhau vào các class. Ví dụ, hai controllers có thể phụ thuộc vào implementations khác nhau của Illuminate\Contracts\Filesystem\Filesystem . Laravel cung cấp một interface đơn giản và liền mạch cho việc khai báo hành vi này:

    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');
              });
    

    Tagging

     

    Thỉnh thoảng, bạn cần giải quyết tất cả các "category" của binding. Ví dụ, có lẽ bạn đang xây dụng một tập hợp báo cáo mà sẽ nhận một mảng danh sách các implementations khác nhau của interface Report. Sau khi đăng ký Report implementations, bạn có thể gán chúng vào một tag sử dụng phương thức tag:

    $this->app->bind('SpeedReport', function () {
        //
    });
    
    $this->app->bind('MemoryReport', function () {
        //
    });
    
    $this->app->tag(['SpeedReport', 'MemoryReport'], 'reports');
    

    Khi service đã được tag, bạn có thể dễ dàng resolve chúng qua phương thức tagged:

    $this->app->bind('ReportAggregator', function ($app) {
        return new ReportAggregator($app->tagged('reports'));
    });
    

    Resolving

    The make Method

     

    Bạn có thể sử dụng phương thức make để resolve một class instance ra khỏi container. Phương thức make nhận tên class hay interface bạn muốn thực hiện resolve:

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

    Nếu bạn đang ở ví trị mà code của bạn không truy cập được biến $app, bạn có thể sử dụng helper global resolve:

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

    Nếu một số dependencies của class của bạn không thể resolve được thông qua container, bạn có thể inject chúng bằng cách chuyển chúng thành mảng liên kết qua phương thức makeWith:

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

     

    Automatic Injection

     

    Ngoài ra, và cũng quang trọng, bạn có thể đơn giản "type-hint" dependency vào trong hàm constructor của class nó sẽ được resolved bởi container, gồm , , , , và còn nữa. Trong thực tế, đây là cách giải quyết đối tượng của bạn sẽ được giải quyết bởi container.

    Ví dụ, bạn có thể type-hint một repository được định nghĩa bởi ứng dụng trong hàm khởi tạo constructor của controller. Repository này sẽ tự động được resolv và inject vào class:

    <?php
    
    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)
        {
            //
        }
    }
    

     

    Container Events

     

    Service container sẽ bắn ra các event mỗi khi nó thực hiện resolves một đối tượng. Bạn có thể listen các event qua phương thức resolving:

    $this->app->resolving(function ($object, $app) {
        // Called when container resolves object of any type...
    });
    
    $this->app->resolving(HelpSpot\API::class, function ($api, $app) {
        // Called when container resolves objects of type "HelpSpot\API"...
    });
    

    Như bạn có thể thấy, đối tượng đang được resolve sẽ truyền lại vào trong hàm callback, cho phép bạn thiết lập các thuộc tính bổ sung nào vào trong object trước khi được trả lại cho bên sử dụng nó.

    Tài liệu:

    CÓ THỂ BẠN QUAN TÂM

    Bài Viết Cùng Chuyên Mục

    XEM THÊM
    thumbnail

    Kubernetes bài 6 - Vận hành k8s Day-Two Operations và Quản trị bằng GitOps

    22.05.2026

    Khi cụm Kubernetes của bạn đã được bảo mật cấu hình, tối ưu tài nguyên và thiết lập tự phục hồi, câu hỏi đặt ra là làm sao để duy trì sự ổn định đó trong nhiều năm tiếp theo mà không bị phụ thuộc

    thumbnail

    Kubernetes bài 5 - bảo mật Cloud Native và chuẩn DevSecOps cho K8s

    22.05.2026

    Việc siết chặt an ninh (Hardening) không phải là cấu hình một vài thông số rồi bỏ đó, mà là một tư duy phòng thủ chiều sâu.

    thumbnail

    Kubernetes bài 4 - Tối ưu Resource Auto-Healing và Scale Zero-Downtime

    22.05.2026

    Bài viết này sẽ đi sâu vào các cơ chế ở tầng Kernel giúp hệ thống tự phục hồi, chống lại các đợt tấn công cạn kiệt tài nguyên và cập nhật phiên bản mới mà người dùng không hề hay biết.

    thumbnail

    Kubernetes bài 3 - Bảo mật cấu hình k8s và config Security trên Production

    22.05.2026

    Kubernetes giải quyết bài toán này bằng hai đối tượng chuyên biệt nhưng nếu không hiểu rõ bản chất bảo mật ở tầng dưới, bạn đang tự tay dâng toàn bộ chìa khóa hệ thống cho hacker.

    thumbnail

    Kubernetes bài 2 - Mạng lưới k8s và luồng Traffic ở Packet Level

    22.05.2026

    Pod không chỉ là một container: Rất nhiều người nhầm lẫn Pod 1-1 với Container. Thực chất, Pod là đơn vị triển khai nhỏ nhất, có thể chứa một hoặc nhiều container

    Mục lục bài viết