Làm cách nào để tôi đưa các phụ thuộc phương thức của bộ điều khiển vào Laravel?

Trong bài viết mới này về cách xây dựng các ứng dụng web bằng PHP Laravel, tôi sẽ giới thiệu nguyên tắc tiêm phụ thuộc, còn được gọi là bộ chứa Inversion of Control [IoC], và khám phá cách PHP Laravel sử dụng nó trong nội bộ và cách nó cung cấp khái niệm này cho các nhà phát triển ứng dụng

Dependency injection là một kỹ thuật lập trình cho phép bạn tách rời các thành phần phần mềm của mình với nhau

Khi bạn đang xây dựng các ứng dụng quy mô lớn, bạn sẽ thường xuyên gặp phải các trường hợp khi một lớp yêu cầu một lớp dịch vụ khác hoạt động chính xác. Khi bạn cho phép lớp tạo các phụ thuộc của riêng mình, bạn đang áp đặt sự liên kết giữa các lớp của mình và làm cho chúng phụ thuộc chặt chẽ vào nhau

Kết quả của sự liên kết chặt chẽ này dẫn đến những hệ quả sau

  • Mã khó kiểm tra hơn
  • Mã khó bảo trì hơn

Đây là một ví dụ cho thấy cách một lớp khởi tạo các phụ thuộc của chính nó

class InvoiceController extends Controller
{
    protected PaymentService $paymentService;

    public function __construct[]
    {
        $this->paymentService = new PaymentService[];
    }
}

Đó là lý do tại sao bạn có nhu cầu tiêm phụ thuộc/bộ chứa IoC để đảo ngược luồng khởi tạo đối tượng. Thay vì một lớp khởi tạo và quản lý các phần phụ thuộc của chính nó, IoC Container thay vào đó chuẩn bị và đưa các phần phụ thuộc đó vào các lớp cần chúng

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}

Các lớp có tùy chọn chấp nhận triển khai cụ thể hoặc giao diện được thay thế bằng triển khai cụ thể khi chạy

Dependency injection thuộc về các nguyên tắc RẮN [https. // vi. wikipedia. org/wiki/SOLID] nhằm mục đích tăng khả năng sử dụng lại mã của bạn. Nó đáp ứng mục tiêu này bằng cách tách quá trình tạo ra một đối tượng khỏi việc sử dụng nó. Do đó, bạn có thể thay thế các phụ thuộc mà không cần thay đổi lớp sử dụng chúng. Nó cũng làm giảm khả năng bạn cần sửa đổi một lớp chỉ vì một trong các thành phần phụ thuộc của nó đã thay đổi

Có nhiều loại tiêm phụ thuộc khác nhau, như tiêm setter, tiêm hàm xây dựng, tiêm phương thức và các loại khác. Tôi sẽ tập trung vào constructor injection trong suốt bài viết này

Một phụ thuộc chỉ là một đối tượng khác mà lớp của bạn cần để hoạt động, vì vậy nếu bạn có một lớp mô hình tìm nạp dữ liệu từ một đối tượng cơ sở dữ liệu, bạn có thể nói rằng lớp mô hình có một phụ thuộc vào đối tượng cơ sở dữ liệu đó

Bốn vai trò chính trong Dependency Injection

Để triển khai phép nội xạ phụ thuộc vào mã của bạn, có bốn vai trò chính mà bạn cần biết về

  • Dịch vụ bạn muốn sử dụng, chẳng hạn như dịch vụ thanh toán hoặc dịch vụ email
  • Khách hàng sử dụng dịch vụ. Đây là lớp mà bạn sẽ đưa dịch vụ vào
  • Một giao diện được khách hàng sử dụng và được triển khai bởi dịch vụ. Đây là tùy chọn. Bạn có thể thêm một lớp cụ thể mà không cần giao diện. Nhưng bằng cách đưa vào một giao diện, bạn có cơ hội trao đổi các cài đặt cụ thể trong thời gian chạy
  • Trình tiêm tạo một phiên bản dịch vụ và đưa nó vào máy khách. Điều này thường được gọi là một thùng chứa tiêm phụ thuộc. Trách nhiệm của nó là quản lý khởi tạo đối tượng và theo dõi các phụ thuộc của chúng

Bốn vai trò trên là bắt buộc để triển khai thành công và sử dụng phép nội xạ phụ thuộc trong ứng dụng của bạn. Vai trò thứ tư, kim phun, là điều bạn không cần phải lo lắng. Thông thường, hầu hết mọi khung công tác back-end đều cung cấp cho bạn một trình tiêm hoặc trình tiêm phụ thuộc, vùng chứa

Người tiêm là bộ não đằng sau khái niệm tiêm phụ thuộc. Chẳng hạn, một khung cung cấp cho bạn phương tiện để đăng ký một phụ thuộc. Khi khung phát hiện một lớp yêu cầu phần phụ thuộc đã đăng ký, nó sẽ sử dụng trình tiêm của nó để khởi tạo phần phụ thuộc và đưa nó vào lớp yêu cầu nó

Bây giờ bạn đã có ý tưởng về việc tiêm phụ thuộc là gì, hãy xem cách PHP Laravel triển khai nó

Nội xạ phụ thuộc đơn giản có nghĩa là sự phụ thuộc được đẩy vào lớp từ bên ngoài. Điều này có nghĩa là bạn không nên khởi tạo các phụ thuộc bằng cách sử dụng toán tử mới từ bên trong lớp, mà thay vào đó, hãy lấy nó làm tham số hàm tạo hoặc thông qua trình thiết lập

Cách Laravel triển khai Dependency Injection

IoC Container nằm ở trung tâm của framework PHP Laravel. Laravel đi kèm với một Service Container chịu trách nhiệm quản lý các phụ thuộc trong ứng dụng và đưa chúng vào bất cứ nơi nào cần thiết

Vùng chứa cung cấp cho bạn cơ hội để xác định các ràng buộc với các lớp và giao diện. Đồng thời, nó có một tính năng độc đáo được gọi là giúp vùng chứa giải quyết các phụ thuộc mà không cần đăng ký chúng. Nó có thể làm như vậy với điều kiện là phần phụ thuộc không có phần phụ thuộc nào khác hoặc có một số phần phụ thuộc mà bộ chứa đã biết cách khởi tạo rồi

Bộ chứa dịch vụ là một dịch vụ của Laravel cho phép bạn nói với Laravel cách một lớp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
6 đối tượng cần được xây dựng và sau đó Laravel có thể tìm ra nó từ đó

Tiêm phụ thuộc đơn giản

Hãy xem một ví dụ để cho bạn thấy cách hoạt động của phép tiêm phụ thuộc trong ứng dụng Laravel. Liệt kê 1 Hiển thị toàn bộ mã nguồn cho ví dụ này

Liệt kê 1. Ví dụ tiêm phụ thuộc đơn giản

public class PaymentService
{
    public function doPayment []
    {
        // ...
    }
}

class PaymentController extends Controller
{
    public function __construct [protected PaymentService $paymentService]
    {

    }

    public function payment []
    {
        // $paymentService
    }
}

Đầu tiên, xác định một

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7 có chứa một phương thức tên là
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
8. Cuối cùng, bạn sẽ đặt mã chịu trách nhiệm thực hiện thanh toán sau khi thanh toán hoặc mua hàng

Tiếp theo, bên trong

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9, xác định hàm tạo chấp nhận làm tham số đầu vào cho đối tượng
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7. Phương thức hành động
public class PaymentService
{
    public function doPayment []
    {
        // ...
    }
}

class PaymentController extends Controller
{
    public function __construct [protected PaymentService $paymentService]
    {

    }

    public function payment []
    {
        // $paymentService
    }
}
1 sử dụng đối tượng
public class PaymentService
{
    public function doPayment []
    {
        // ...
    }
}

class PaymentController extends Controller
{
    public function __construct [protected PaymentService $paymentService]
    {

    }

    public function payment []
    {
        // $paymentService
    }
}
2 để thực hiện thanh toán

Khi bạn gửi yêu cầu đến phương thức

public class PaymentService
{
    public function doPayment []
    {
        // ...
    }
}

class PaymentController extends Controller
{
    public function __construct [protected PaymentService $paymentService]
    {

    }

    public function payment []
    {
        // $paymentService
    }
}
1, Laravel sẽ thực hiện nhiều tác vụ phía sau hậu trường. Một trong những nhiệm vụ đó là khởi tạo lớp
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9. Trong khi khởi tạo nó, nó nhận thấy rằng hàm tạo yêu cầu một phụ thuộc

Laravel sử dụng Service Container của nó để tra cứu các phụ thuộc. Hiện tại, bạn chưa hướng dẫn Laravel về cách khởi tạo phụ thuộc

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7. Tuy nhiên, Laravel đủ thông minh để giải quyết sự phụ thuộc này và đưa nó vào hàm tạo
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9.
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7 không có phụ thuộc nào khác, vì vậy thật dễ dàng để Laravel khởi tạo nó và tạo một đối tượng từ nó

Sau khi Laravel khởi tạo lớp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7, nó sẽ khởi tạo một đối tượng mới ra khỏi lớp
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9 bằng cách sử dụng hàm tạo của nó và chuyển phụ thuộc cần thiết

Bạn đã thấy cách Laravel Dependency Injection hoạt động đối với các trường hợp đơn giản trong đó một lớp có sự phụ thuộc vào một lớp khác mà không có sự phụ thuộc nào khác. Điều gì sẽ xảy ra khi bản thân

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7 có sự phụ thuộc?

Thêm phụ thuộc vào các phụ thuộc khác

Trong phần này, bạn sẽ khám phá điều gì sẽ xảy ra khi một phụ thuộc có một phụ thuộc bắt buộc. Laravel hoạt động như thế nào?

Hãy xem với một ví dụ khác, được hiển thị trong Liệt kê 2

Liệt kê 2. Dependency Injection với ví dụ phụ thuộc

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
8

Lớp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7 hiện định nghĩa một hàm tạo chấp nhận một phụ thuộc của kiểu chuỗi có tên là
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
82. Trong trường hợp này, Laravel sẽ không thể tự khởi tạo
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7 mà không có sự trợ giúp từ phía bạn. Nguyên nhân?

Bạn phải cung cấp cho Laravel các hướng dẫn bổ sung về cách khởi tạo

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7

Bên trong tệp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
85, bạn đăng ký một ràng buộc để cho Laravel biết cách khởi tạo một đối tượng mới của
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
5

Cuộc gọi phương thức

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
87 trả về một thể hiện của lớp
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
88. Đến lượt nó, lớp này mở rộng lớp
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
89. Do đó, phương thức
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
87 cho phép bạn làm việc trực tiếp với Laravel Service Container

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
51 xác định phương thức
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
52, cho phép bạn xác định ràng buộc mới bên trong Bộ chứa dịch vụ

Phương thức

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
52 chấp nhận làm tham số đầu vào đầu tiên, tên hoặc loại của phần phụ thuộc mà bạn muốn xác định ràng buộc cho. Đối số thứ hai là Đóng PHP [https. //www. php. net/manual/en/class. Khép kín. php]. Mã khởi tạo và trả về một phiên bản mới của
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7 bằng cách cung cấp khóa bí mật chính xác theo yêu cầu của dịch vụ

Khi đến lúc Laravel đưa

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7 vào
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9, nó sẽ kiểm tra xem bạn đã xác định một ràng buộc cho sự phụ thuộc này chưa. Nếu nó tìm thấy, nó sẽ thực thi và chạy bao đóng để trả về một thể hiện của sự phụ thuộc này

Tóm lại, bạn không chỉ có cơ hội sử dụng Bộ chứa dịch vụ để khởi tạo và chèn các phụ thuộc mà còn hướng dẫn nó cách khởi tạo các phụ thuộc

Sẽ phức tạp hơn một chút khi bạn có nhiều triển khai cụ thể của cùng một dịch vụ. Hãy xem cách bạn hướng dẫn Laravel Service Container xử lý sự phức tạp này

Một sự phụ thuộc với nhiều triển khai cụ thể

Thông thường, bạn cần kết nối với nhiều cổng thanh toán cùng một lúc. Tùy thuộc vào tùy chọn của người dùng hoặc các tiêu chí khác trong ứng dụng của bạn, bạn có thể cần có nhiều triển khai cụ thể của một dịch vụ và sẵn sàng sử dụng một triển khai cụ thể tại một thời điểm hoặc cả hai cùng nhau tùy thuộc vào một số logic

Liệt kê 3 hiển thị toàn bộ mã cho ví dụ này

Liệt kê 3. Nhiều triển khai cụ thể

public class PaymentService
{
    public function doPayment []
    {
        // ...
    }
}

class PaymentController extends Controller
{
    public function __construct [protected PaymentService $paymentService]
    {

    }

    public function payment []
    {
        // $paymentService
    }
}
6

Bạn bắt đầu bằng cách xác định giao diện

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
57 [https. //www. thiên thần. com/blog/what-interface-php]. Giao diện này chỉ ra phương thức nào cần tồn tại và được triển khai trên các cổng thanh toán khác nhau có sẵn trong ứng dụng

Tiếp theo, xác định hai dịch vụ thanh toán mới.

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
58 và
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
59. Mỗi dịch vụ triển khai giao diện
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
57 và cung cấp cách triển khai cụ thể khác nhau cho cổng thanh toán tương ứng của nó.
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
58 kết nối với dịch vụ Paypal và cái sau kết nối với dịch vụ Stripe

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9 định nghĩa giao diện
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
57 là một phụ thuộc. Trong trường hợp này, bộ điều khiển đang yêu cầu một giao diện thay vì triển khai cụ thể thực tế. Điều xảy ra trong thời gian chạy là dựa trên cách bạn định cấu hình Bộ chứa dịch vụ, Laravel đưa một triển khai cụ thể nhất định vào bộ điều khiển này để thay thế phiên bản giao diện

Cuối cùng, bạn hướng dẫn Laravel Service Container trả về một phiên bản cụ thể của

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
57 dựa trên tham số yêu cầu có tên là cổng. Nếu nó có giá trị sọc, thì bạn trả về
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
59, nếu không, bạn trả về
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
58. Đây là một triển khai đơn giản để minh họa ID. Bạn có thể mở rộng nó hơn nữa theo cách nó phù hợp với nhu cầu của ứng dụng của bạn

Sử dụng các giao diện làm phần phụ thuộc cho phép bạn trao đổi các triển khai trong thời gian chạy mà không cần nỗ lực. Ngoài ra, theo cách này, bạn không phải thay đổi toàn bộ nguồn trong trường hợp dịch vụ PayPal hoặc Stripe đã thay đổi. Trong tương lai, bạn có thể cần thêm một dịch vụ thanh toán bổ sung và điều này có thể dễ dàng thực hiện bằng cách thêm triển khai mới của giao diện

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
57 và đăng ký nó với phương thức
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
52 của Bộ chứa dịch vụ trong Laravel

Bây giờ tôi đã đề cập đến một số trường hợp sử dụng của Laravel Service Container và các cách khác nhau để cung cấp phép nội xạ phụ thuộc trong ứng dụng Laravel, hãy xem việc kiểm tra các phụ thuộc giả dễ dàng như thế nào, đặc biệt là khi sử dụng chúng làm giao diện

Kiểm tra mã với phụ thuộc

Tiêm phụ thuộc có tác dụng phụ chính là viết mã sạch hơn và tốt hơn. Khi mã hóa dựa trên các giao diện, bạn có thể hoán đổi triển khai trong khi kiểm tra và có thể tách lớp đang kiểm tra mà không cần kiểm tra các phần phụ thuộc của nó. Giả sử rằng các phần phụ thuộc được kiểm tra riêng biệt, bạn có thể chỉ cần giả định chúng;

Laravel cung cấp thử nghiệm PHPUnit ngay lập tức. Gần đây, một thư viện thử nghiệm mới đã xuất hiện, được gọi là Pest PHP. Thư viện này nội bộ dựa trên PHPUnit. Tuy nhiên, nó mang lại trải nghiệm rõ ràng hơn và dễ dàng hơn để kiểm tra và mong đợi/khẳng định kết quả kiểm tra

Chế nhạo một đối tượng có nghĩa là thay thế hoặc che đậy việc triển khai thực tế của một lớp bằng một lớp giả hoặc lớp giả khác hầu như không có chức năng. Điểm chung duy nhất giữa một đối tượng và mô hình của nó là bản thiết kế của các phương thức và chức năng. Một đối tượng giả có thể được sử dụng thay cho đối tượng ban đầu, đặc biệt là khi viết bài kiểm tra. Tuy nhiên, bạn, với tư cách là nhà phát triển, có thể kiểm soát hành vi của đối tượng giả bằng cách quyết định phương thức nào sẽ được gọi, tham số nào sẽ truyền cho các phương thức đó và giá trị trả về mà các phương thức đó có thể trả về

Tóm lại, các đối tượng chế nhạo có những lợi ích sau

  • Một lớp đang được kiểm tra có thể được tách biệt khỏi các phần phụ thuộc của nó
  • Các bài kiểm tra chạy nhanh, đặc biệt nếu bạn đang chế nhạo một lớp tương tác với cơ sở dữ liệu hoặc I/O

Bất kể bạn đang sử dụng thư viện thử nghiệm nào; . Bằng cách thêm một đối tượng giả, bạn đang tách lớp đang kiểm tra khỏi các phần phụ thuộc của nó và giả sử rằng tất cả các lệnh gọi phương thức của phần phụ thuộc đều hoạt động như mong đợi. Tất nhiên, điều này giả định rằng bạn đã viết đủ các trường hợp thử nghiệm để bao quát tính chính xác của các đối tượng phụ thuộc đó

Để tạo một đối tượng giả lập khi sử dụng Pest PHP, bạn cần cài đặt plug-in tổng hợp. Theo liên kết này để tìm hiểu cách thiết lập môi trường của bạn https. // sâu bệnhphp. com/docs/plugins/mock

Đây là cách tạo và sử dụng các đối tượng giả trong PHPUnit [https. //phpunit. de/thủ công/6. 5/vi/kiểm tra nhân đôi. html]

Hãy xem một ví dụ về cách giả lập một phụ thuộc bằng thư viện PHPUnit

Để tạo một bài kiểm tra tính năng mới trong Laravel, hãy chạy lệnh sau

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9

Lệnh tạo một tệp

public class PaymentService
{
    public function doPayment []
    {
        // ...
    }
}

class PaymentController extends Controller
{
    public function __construct [protected PaymentService $paymentService]
    {

    }

    public function payment []
    {
        // $paymentService
    }
}
69 mới bên trong thư mục
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
90. Liệt kê 4 hiển thị toàn bộ mã trường hợp thử nghiệm

Liệt kê 4. Kiểm tra mã với các phụ thuộc

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
2

Đối với trường hợp thử nghiệm này, tôi sẽ sử dụng ví dụ về triển khai phép nội xạ phụ thuộc bằng giao diện

Sử dụng phép nội xạ phụ thuộc giúp đơn giản hóa việc tạo các bản thử nghiệm kép [thường được gọi là "mô hình"]. Nếu bạn chuyển các phụ thuộc vào các lớp, bạn có thể dễ dàng chuyển qua triển khai kép thử nghiệm

Không thể tạo các bản kiểm tra kép cho các phụ thuộc được mã hóa cứng

bắt đầu bằng

  • Tạo một mô hình mới cho lớp
    class InvoiceService
    {
        public function __construct[
          protected PaymentService $paymentService] { }
    }
    
    58
  • Đặt kỳ vọng cho đối tượng giả. Chẳng hạn, bạn hướng dẫn đối tượng mô phỏng rằng phương thức
    class InvoiceService
    {
        public function __construct[
          protected PaymentService $paymentService] { }
    }
    
    8 sẽ được thực thi một lần và sẽ trả về
    class InvoiceService
    {
        public function __construct[
          protected PaymentService $paymentService] { }
    }
    
    93. Đây là nơi bạn kiểm soát những gì cần chuyển đến các phương thức và những gì cần trả về. Bạn có toàn quyền kiểm soát những gì được truyền và trả về từ các phương thức
  • Bạn thay thế ràng buộc cho giao diện
    class InvoiceService
    {
        public function __construct[
          protected PaymentService $paymentService] { }
    }
    
    57 bằng đối tượng mẫu giả. Điều này thay thế bất kỳ ràng buộc nào trước đó được đặt bên trong
    class InvoiceService
    {
        public function __construct[
          protected PaymentService $paymentService] { }
    }
    
    95 [như đã trình bày trước đây trong bài viết này]. Trong khi chạy thử nghiệm, bạn muốn thay thế ánh xạ Vùng chứa dịch vụ để sử dụng đối tượng giả của mình
  • Cuối cùng, bạn gửi một yêu cầu GET đến điểm cuối
    class InvoiceService
    {
        public function __construct[
          protected PaymentService $paymentService] { }
    }
    
    96

Xác định điểm cuối mới này bên trong tệp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
97 như sau

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
0

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9 nên được định nghĩa là một bộ điều khiển có thể gọi được, như trong Liệt kê 5

Liệt kê 5. lớp PaymentController

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
2

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9 có sự phụ thuộc vào giao diện
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
57. Khi chạy thử nghiệm, tham số
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
21 được thay thế bằng đối tượng giả định
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
58. Bằng cách sử dụng một bản mô phỏng, không có gì thay đổi về cách thức mà
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
9 gọi các phương thức trên giao diện
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
57. Một đối tượng giả đảm bảo rằng mã tiếp tục hoạt động với cùng một bản thiết kế bất kể việc triển khai cụ thể thực tế như thế nào

Bây giờ bạn đã biết lợi ích của việc tiêm phụ thuộc và Laravel Service Container, hãy cùng khám phá những tùy chọn ràng buộc nào mà service container cung cấp

Ràng buộc vùng chứa dịch vụ

Laravel Service Container cung cấp một số cách để liên kết và đăng ký các phụ thuộc. Bạn đã biết cách sử dụng phương pháp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
25 để ràng buộc một phụ thuộc trong ứng dụng. Bộ chứa dịch vụ cung cấp các cách khác để ràng buộc các phụ thuộc. Tài liệu về Laravel thực hiện rất tốt việc trình bày chi tiết tất cả []. Tuy nhiên, tôi muốn làm sáng tỏ ba kỹ thuật đóng sách chính mà bạn có thể thấy mình sử dụng hầu hết thời gian.

Đóng sách thủ công

Bạn đã thấy cách liên kết thủ công xảy ra bằng phương pháp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
52. Mỗi khi Laravel yêu cầu một phần phụ thuộc được đăng ký bằng phương thức
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
52, nó sẽ yêu cầu Bộ chứa dịch vụ tạo và trả về một phiên bản mới của phần phụ thuộc đã đăng ký. Trong một số trường hợp, điều này có thể không hiệu quả, đặc biệt khi bạn đang tạo các lớp tài nguyên đắt tiền

Đây là một ví dụ về việc sử dụng phương pháp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
52

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
0

Chẳng hạn, bên trong chức năng Đóng, bạn có thể truy xuất các giá trị từ cấu hình ứng dụng. Nếu một phần phụ thuộc yêu cầu một phần phụ thuộc khác, bạn thậm chí có thể yêu cầu một phần phụ thuộc từ Bộ chứa dịch vụ khi đang ở trong chức năng Đóng

Hãy xem ràng buộc đơn lẻ khác với ràng buộc thủ công như thế nào

Ràng buộc đơn

Liên kết singleton đảm bảo rằng, khi một phần phụ thuộc được đăng ký dưới dạng một singleton, sẽ có một và chỉ một phiên bản cho mỗi vòng đời yêu cầu/phản hồi. Trái ngược với ràng buộc thủ công, mọi yêu cầu truy xuất một phần phụ thuộc từ Bộ chứa dịch vụ đều dẫn đến việc tạo một phiên bản mới của phần phụ thuộc đó. Với ràng buộc đơn lẻ, một trường hợp duy nhất tồn tại. Điều này hữu ích hơn khi bạn có các dịch vụ hoặc lớp quá đắt để tiếp tục tạo chúng khi chúng được yêu cầu

Đây là một ví dụ về việc sử dụng phương pháp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
29

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
1

Mỗi khi có nhu cầu về một phiên bản của

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7, cùng một đối tượng phiên bản chính xác sẽ được trả về trong vòng đời yêu cầu/phản hồi

Ràng buộc sơ thẩm

Liên kết thể hiện giống như liên kết đơn lẻ, ngoại trừ việc bạn tạo một phiên bản phụ thuộc mới và hướng dẫn Laravel Service Container luôn trả về phiên bản này

Đây là một ví dụ về việc sử dụng phương pháp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
01

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
2

Sự khác biệt chính giữa liên kết phiên bản và hai hình thức liên kết khác là bạn luôn tạo phiên bản mới và thêm phiên bản đó vào Bộ chứa dịch vụ. Trong trường hợp liên kết thủ công hoặc đơn lẻ, chỉ khi ứng dụng yêu cầu một phiên bản của phần phụ thuộc thì hàm Đóng mới thực thi và trả về một phiên bản. Hãy nghĩ về nó như một điều ràng buộc sớm so với ràng buộc muộn

Như tôi đã đề cập, có nhiều cách khác để mở rộng Bộ chứa dịch vụ và đăng ký các phụ thuộc. Bạn có thể đọc tất cả các chi tiết tại trang web tài liệu Laravel

Giải quyết các phụ thuộc vùng chứa dịch vụ

Bộ chứa dịch vụ cung cấp một số cách để khởi tạo các phiên bản phụ thuộc. Tôi sẽ liệt kê một số cách mà tôi thường sử dụng

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
3

Bạn có thể chỉ cần sử dụng phương pháp

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
87 để giải quyết và khởi tạo một phụ thuộc

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
4

Một phương pháp khác để khởi tạo các đối tượng là sử dụng phương thức

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
03 được định nghĩa trên đối tượng
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
87

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
5

Hàm

class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
05 là một hàm trợ giúp khởi tạo một đối tượng dựa trên tên ánh xạ được truyền cho nó. Trong trường hợp này, bạn đang yêu cầu từ Bộ chứa dịch vụ khởi tạo một thể hiện của lớp
class InvoiceService
{
    public function __construct[
      protected PaymentService $paymentService] { }
}
7

Cuối cùng, các phụ thuộc cũng có thể được khởi tạo sau khi được đưa vào các lớp bên trong hàm tạo của chúng. Bạn sử dụng loại giải pháp này hầu hết thời gian khi phát triển với Laravel

Phần kết luận

PHP Laravel là một framework PHP phong phú với nhiều chủ đề để thảo luận và học hỏi. Trong loạt bài này, tôi đang cố gắng trình bày càng nhiều tính năng và khái niệm của Laravel càng tốt để giúp bạn xây dựng các ứng dụng tốt hơn bằng cách sử dụng framework này. Trong các bài viết tiếp theo, tôi sẽ tiếp tục khám phá từng bước một về các tính năng của Laravel

Làm cách nào để sử dụng phép nội xạ phụ thuộc trong Laravel?

Bộ chứa dịch vụ Laravel là một công cụ mạnh mẽ để quản lý các lớp phụ thuộc và thực hiện phép nội xạ phụ thuộc. Tiêm phụ thuộc là một cụm từ ưa thích về cơ bản có nghĩa là điều này. các phần phụ thuộc của lớp được "đưa" vào lớp thông qua hàm tạo hoặc, trong một số trường hợp, các phương thức "setter" .

Laravel có tiêm phụ thuộc không?

Trong Laravel, phép nội xạ phụ thuộc là quá trình đưa các phần phụ thuộc của lớp vào một lớp thông qua hàm tạo hoặc phương thức setter . Điều này cho phép mã của bạn trông sạch sẽ và chạy nhanh hơn. Dependency injection liên quan đến việc sử dụng một Laravel service container, một container được sử dụng để quản lý các lớp phụ thuộc.

Đó là cách đúng đắn để tiêm phụ thuộc?

Có ba cách phổ biến để thêm phụ thuộc. .
Xây dựng tiêm. Sự phụ thuộc được chuyển đến đối tượng thông qua hàm tạo của nó chấp nhận một giao diện làm đối số. .
phương pháp tiêm. Một. k. a. tiêm dựa trên giao diện. .
Tiêm tài sản. Một. k. a. tiêm setter

Có bao nhiêu cách bạn có thể tiêm phụ thuộc?

ba loại tiêm phụ thuộc — tiêm hàm tạo, tiêm phương thức và tiêm thuộc tính.

Chủ Đề