Quay lại

Tìm hiểu về Constructor và Method injection trong Laravel Chuyên mục PHP và Laravel    2023-07-27    916 Lượt xem    62 Lượt thích    comment-3 Created with Sketch Beta. 0 Bình luận

Tìm hiểu về Constructor và Method injection trong Laravel

Để hiểu cách thức hoạt động của dependency injection trong Laravel, chúng ta hãy cùng tìm hiểu xem dependency injection thực sự là gì, trong công nghệ phần mềm.

Từ định nghĩa Wikipedia về dependency injection:

In software engineering, dependency injection is a technique whereby one object supplies the dependencies of another object.

Dịch tạm như thế này : 

Trong công nghệ phần mềm, dependency injection là một kỹ thuật theo đó một đối tượng cung cấp các phụ thuộc của đối tượng khác.

Nói một cách đơn giản, dependency injection là một cách tách biệt việc tạo ra các client’s dependencies ra khỏi client’s behavior. Hãy lấy một ví dụ để hiểu điều này.

<?php

namespace App;

class Client {
    // Internal reference to the service used by this client
    private $service;

    // Constructor
    public function _construct() {
        $this->service = new UserService();
    }

    // Method within this client that uses the services
    public function greet() {
        return "Hello " + $this->service->getName();
    }
}

Trong ví dụ trên, class Client muốn sử dụng một service khác gọi là UserService bằng cách khởi tạo một thuộc tính của class là $service sau đó thì thuộc tính này sẽ được gán cho UserService, sau đó có thể truy cập các phương thức khác trong class UserService. Ở đây như bạn có thể thấy, client (class Client) kiểm soát việc triển khai service (class UserService) nào được sử dụng và kiểm soát việc xây dựng nó. Ở đây, lớp Client có sự phụ thuộc ngầm được hard-coded vào UserService.

Nhưng vấn đề là logic nghiệp vụ của bạn có thể thay đổi theo thời gian và bạn có thể muốn sử dụng một số service khác ngoài UserService, chẳng hạn như UserRepository trong lớp cụ thể này. Bạn sẽ làm gì trong trường hợp này? Thay thế phiên bản của UserService bằng phiên bản của UserRepository? Không, điều đó sẽ khiến việc kiểm tra lớp trở nên khó khăn hơn và việc trao đổi sự phụ thuộc như thế này là hoàn toàn vô căn cứ. Đây là nơi khái niệm về sự phụ thuộc phát huy tác dụng.

Constructor dependency injection

Loại dependency injection đầu tiên mà chúng ta sẽ tìm hiểu là “Constructor dependency injection”. Hãy hiểu nó bằng cách điều chỉnh ví dụ trên.

<?php

namespace App;

class Client {
    // Internal reference to the service used by this client
    private $service;

    // Constructor
    public function _construct(UserService $service) {
        $this->service = $service;
    }

    // Method within this client that uses the services
    public function greet() {
        return "Hello " + $this->service->getName();
    }
}

Như bạn có thể thấy, chúng ta hiện đang “injecting” sự phụ thuộc vào lớp một cách rõ ràng bằng cách type-hinting UserService vào constructor. Lớp Client bây giờ không cần lo lắng về cách service được kết nối với nó. Tất cả những gì nó mong đợi là UserService instance. Chúng ta không cần phải chỉnh sửa Client class  cho phần phụ thuộc của nó, chúng ta vừa cung cấp cho nó những gì nó cần.

Bây giờ, hãy hiểu cách Laravel đang sử dụng dependency injection trong framework của chính nó.

<?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ụ này, UserController cần truy xuất người dùng từ data source. Vì vậy, chúng ta sẽ đưa vào một service có thể truy xuất người dùng. Trong bối cảnh này, UserRepository của chúng ta rất có thể sử dụng Eloquent để truy xuất thông tin người dùng từ cơ sở dữ liệu. Tuy nhiên, vì kho lưu trữ đã được đưa vào, chúng ta có thể dễ dàng trao đổi nó với một triển khai khác. Chúng ta cũng có thể dễ dàng "mock" hoặc tạo triển khai dummy data của UserRepository khi thử chạy test ứng dụng.

Laravel sử dụng tính năng đặc biệt này của PHP được gọi là Reflection và service container để thực hiện việc dependency injection. Những gì Laravel làm trong ví dụ trên là nó sẽ kiểm tra dependency nào đã được chuyển đến constructor bằng cách sử dụng Reflection và sẽ tự động giải quyết các dependencies cần thiết cho class đó.

Phương pháp trên giải quyết tự động. Nhưng có một cách khác để làm điều này trong Laravel. tức là sử dụng các service container bindings. Vì vậy, chúng ta có thể liên kết lớp UserRepository với lớp UserController như thế này với service provider.

$this->app->bind('App\Http\Controllers\UserController', function ($app) {
    return new HelpSpot\API($app->make('UserRepository'));
});

Method dependency injection

Có thể xảy ra trường hợp bạn chỉ muốn inject dependency vào một phương thức nhất định. Trong các tình huống như vậy, bạn có thể sử dụng dependency trực tiếp vào phương thức theo đó bạn đưa đối tượng vào lớp của mình thông qua phương thức setter thay vì hàm constructor.

<?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 getSettings(UserSettings $settings)
    {
        return $settings->get();
    }
}

Như bạn có thể thấy, trong trường hợp này nếu chúng ta biết rằng phương thức getSettings là phương thức duy nhất sẽ sử dụng UserSettings, thì không nên đưa vào constructor. Vì vậy, thay vào đó, chúng ta đã đưa nó vào ngay phương thức mà Laravel hỗ trợ ngay lập tức.

Bình luận (0)

Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough
Michael Gough

Bài viết liên quan

Learning English Everyday