Magento 2: sử dụng hay không sử dụng ObjectManager trực tiếp?


134

Ok, vì vậy ngày hôm qua chúng tôi đã có một cuộc nói chuyện lớn với những người khác từ cộng đồng Magento liên quan đến việc sử dụng trực tiếp các ObjectManagerlớp / mẫu .

Tôi đã nhận thức được lý do tại sao chúng ta không nên sử dụng ObjectManager trực tiếp, trích dẫn Alan Kent :

Có một số lý do. Mã sẽ hoạt động, nhưng tốt nhất là không tham chiếu trực tiếp lớp ObjectManager.

  • Bởi vì chúng tôi nói như vậy! ;-) (được biểu thị tốt hơn dưới dạng mã nhất quán là mã tốt)
  • Mã này có thể được sử dụng với khung tiêm phụ thuộc khác trong tương lai
  • Việc kiểm tra dễ dàng hơn - bạn chuyển các đối số giả cho lớp được yêu cầu mà không phải cung cấp Trình quản lý đối tượng giả
  • Nó giữ cho các phụ thuộc rõ ràng hơn - rõ ràng những gì mã phụ thuộc vào thông qua danh sách hàm tạo, thay vì có các phụ thuộc ẩn ở giữa mã
  • Nó khuyến khích các lập trình viên suy nghĩ về các khái niệm như đóng gói và mô đun hóa tốt hơn - nếu hàm tạo trở nên lớn, có thể đó là dấu hiệu mã cần tái cấu trúc

Từ những gì tôi đã thấy trong StackExchange, rất nhiều người có xu hướng tìm kiếm giải pháp dễ / ngắn / không được đề xuất, ví dụ như một cái gì đó như thế này:

<?php 
//Get Object Manager Instance
$objectManager = \Magento\Framework\App\ObjectManager::getInstance();

//Load product by product id
$product = $objectManager->create('Magento\Catalog\Model\Product')->load($id);

Thay vì trải qua quá trình đau đớn nhưng được khuyến nghị :

  • tạo ra một mô-đun
  • tuyên bố ưu đãi
  • phụ thuộc tiêm
  • tuyên bố một phương thức công khai

Tuy nhiên, và ở đây tiến thoái lưỡng nan, các tệp lõi Magento 2 thường gọi trực tiếp cho ObjectManager . Một ví dụ nhanh có thể được tìm thấy ở đây: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

Vì vậy, đây là những câu hỏi của tôi:

  • Tại sao Magento làm những gì họ khuyên chúng ta không nên làm? Điều đó có nghĩa là có một số trường hợp chúng ta nên sử dụng ObjectManagertrực tiếp ? Nếu vậy, những trường hợp đó là gì?
  • Là gì hậu quả của việc sử dụng các ObjectManager trực tiếp ?


3
Liên kết có liên quan: mwop.net/blog/2016-04-26-on-locators.html . Các bit có liên quan của nó sẽ là The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. Mà nó áp dụng cho M2, quá. Cũng kiểm tra There are valid use casesphần, một lần nữa, áp dụng ở đây, quá.
phải

3
Đã có một khoảng thời gian phát triển M2 khi OM đã ở đó, nhưng toàn bộ magento vẫn chưa được thay đổi để sử dụng phương thức tiêm xây dựng. Vào thời điểm đó, nhiều người đã thay thế Mage :: getSingleton () bằng ObjectManager :: getInstance () -> get (). Hầu hết các tập quán như vậy đã được giới thiệu vào thời kỳ đó. Sau đó, tất cả các lệnh gọi Mage :: getSingleton () đã được thay thế bằng cách tiêm constructor bằng một công cụ, nhưng công cụ không nhận ra ObjectManager :: getInstance (), do đó, nó không thay thế nó bằng cách tiêm constructor.
Anton Kril

3
Bản sao có thể có của phiên bản Người trợ giúp Magento 2
Teja Bhagavan Kollepara

3
@TejabhagavanKollepara bạn đã đọc cả hai câu hỏi? Có sự tương đồng nhưng khác xa với việc trùng lặp với nhau
Raphael tại Digital Pianism

Câu trả lời:


98

Bạn không nên sử dụng ObjectManager trực tiếp!

Ngoại lệ từ quy tắc là:

  • trong các phương pháp kỳ diệu tĩnh như __wakeup, serialize, vv
  • trong trường hợp bạn nên tạo khả năng tương thích ngược của hàm tạo
  • trong phạm vi toàn cầu, giống như trong đồ đạc của thử nghiệm tích hợp.
  • trong lớp chỉ cần tạo đối tượng như nhà máy, proxy, v.v.

2
Tôi biết tôi không bao giờ nên sử dụng nó trực tiếp nhưng tại sao Magento lại làm điều đó? ^^
Raphael tại Digital Pianism

2
trong ví dụ của bạn là để tương thích ngược
KAndy

Có phải những người luôn được gắn cờ là @deprecated?
Raphael tại Digital Pianism


5
oh yeah bạn đời tôi biết nó chỉ là khó hiểu. Có lẽ họ nên nói "đừng làm điều đó nhưng hãy lưu ý rằng có lẽ chúng ta đã để lại một số sai lầm ở đây và đó";)
Raphael tại Digital Pianism

53

Vậy tại sao M2 đôi khi truy cập trực tiếp vào trình quản lý đối tượng khi chúng tôi đề xuất chống lại nó?

Câu trả lời tàn bạo: M2 là một cổng của M1 - không phải là viết lại hoàn chỉnh. Vì vậy, đừng cho rằng tất cả các mã M2 đã được chuyển hoàn hảo (thật không may). Chỉ vì bạn tìm thấy thứ gì đó trong cơ sở mã M2, điều đó không có nghĩa là "cách tốt nhất để làm điều đó". Đôi khi, đó chỉ là "chúng tôi chưa có cách khắc phục".

Ít tàn bạo hơn: Theo các phản hồi khác, đôi khi bạn PHẢI sử dụng nó vì không có sự thay thế nào. Những lần khác, nó có thể là vì lý do tương thích ngược. Và mã khung đôi khi có ý nghĩa khi sử dụng nó trực tiếp, bởi vì nó là mã khung. Nhưng nếu tôi phải đoán mà không nhìn vào mã, nhiều cái thực sự cần được sửa nhưng nó vẫn chưa đủ ưu tiên để làm điều đó.

Chỉ cần nhớ lời khuyên nuôi dạy con tốt: "Trẻ em, hãy làm những gì tôi nói, không phải những gì tôi làm!"


9
trích dẫn tuyệt vời: Trẻ em, làm những gì tôi nói, không phải những gì tôi làm!
sivakumar

Đó không phải là cách nó hoạt động kiddo
Ansyori

Có một cách Magento 2 đề xuất để có một vấn đề phụ thuộc mềm mà không có trình quản lý đối tượng? Tôi có một mô-đun với sự phụ thuộc mềm vào một lớp khác (nó tải một lớp khác nếu mô-đun tồn tại). Tôi không thể DI lớp đó vì sau đó DI sẽ thất bại. Tôi thậm chí không thể DI một Nhà máy cho lớp đó vì nhà máy sẽ thất bại với DI.
Nathan Merrill

50

Bạn không bao giờ nên sử dụng \Magento\Framework\App\ObjectManager::getInstance().
Nó đánh bại mục đích của tiêm phụ thuộc. Chúng tôi đã trở lại Mage::getModel().
Trình quản lý đối tượng chỉ nên được sử dụng trong các nhà máy và sau đó được đưa vào trong một hàm tạo.

Ưu điểm của việc sử dụng này là ít mã để viết. Nhưng điều này không làm cho nó ổn.
Thực tế là cái này vẫn được sử dụng trong lõi, bởi vì nó chưa được tái cấu trúc. Tôi hy vọng nó sẽ được.


5
Vậy cả hai chúng ta đều đồng ý rằng mã Magento đang làm sai phải không?
Raphael tại Digital Pianism

11
đúng. họ sai :).
Marius

Tôi không nghĩ họ đang sử dụng sai. Họ đang sử dụng nó khi cần thiết: khi cần phân giải động (bổ sung, đặc biệt) và khi giữ BC trên các phương pháp không dùng nữa.
nevvermind

2
@nevvermind Sử dụng nhà máy. Bạn sử dụng di.xmlđể tạo một khóa => bản đồ tên lớp và đưa bản đồ đó vào nhà xây dựng của nhà máy và sử dụng nhà máy để khởi tạo lớp thông qua đối tượng
Marius

2
@nevvermind Nhưng ý kiến ​​của nhân viên Magento vượt trội hơn ý kiến ​​của bạn. Bạn có câu trả lời ở trên từ KAndy nói rằng chữ in đậm "bạn không nên sử dụng trình quản lý đối tượng trực tiếp": magento.stackexchange.com/a/117103/146 Tôi đoán rằng loại sương mù trong vấn đề này.
Marius

22

Tại sao Magento làm những gì họ khuyên chúng ta không nên làm? Điều đó có nghĩa là có một số trường hợp chúng ta nên sử dụng ObjectManager trực tiếp? Nếu vậy, những trường hợp đó là gì?

Không biết toàn bộ câu chuyện ở đây là phỏng đoán của tôi:

Trong sự phát triển của M2 đội Magento tại một số giai đoạn chạy một kịch bản tự động thay thế xuất hiện của Mage:getModel(), Mage::getSingleton(), $layout->createBlock(), vv để sử dụng ObjectManager.

Tái cấu trúc sau này nên đã sửa lỗi này để thay vào đó sử dụng phép tiêm phụ thuộc thích hợp nhưng không có đủ thời gian / tài nguyên để chuyển đổi tất cả các lần xuất hiện.

Ngoài ra, nhóm Magento gần đây dường như sử dụng điều này như một cơ chế thoát hiểm. Thay vì phá vỡ một triển khai hiện có (bằng cách cần thay đổi hàm tạo), họ chỉ cần ẩn phần phụ thuộc mới thông qua ObjectManager. Tôi không thể nói rằng tôi đồng ý với cách tiếp cận này - viết mã tệ hơn để tránh phá vỡ BC.

Hậu quả trực tiếp của việc sử dụng ObjectManager trực tiếp là gì?

Tôi nghĩ rằng câu hỏi của bạn đã bao gồm đủ lý do. Nói chung, nó tạo ra một phụ thuộc ẩn, nói cách khác, sự phụ thuộc nằm trong các chi tiết triển khai và không thể nhìn thấy được từ nhà xây dựng.


Thật là mỉa mai vì đã thực hiện đúng cách trước khi phát hành ra công chúng, BC sẽ không phải là một vấn đề gì cả
Robbie Averill

12

Không nên sử dụng Trình quản lý đối tượng trực tiếp!

Ví dụ:

\Magento\Framework\App\ObjectManager::getInstance();

Ngoài ra, nếu bạn đang làm việc với các trình quan sát sự kiện hoặc plugin, bạn không bao giờ nên sử dụng nó trực tiếp.

Bạn có thể sử dụng nó trong Factories, nhưng ngoại trừ việc bạn nên tiêm Trình quản lý đối tượng vào Trình xây dựng trước thì bạn có thể sử dụng đối tượng của nó trong phương thức của mình

Thích sử dụng:

1) khai báo đối tượng riêng tư:

private $_objectManager;

2) tiêm vào hàm tạo và khởi tạo:

public function __construct(
    \Magento\Framework\ObjectManagerInterface $objectmanager
) {
    $this->_objectManager = $objectmanager;
}

3) sử dụng trong một số phương pháp:

public function create() {
    return $this->_objectManager->create(/* ......... */);
}

Câu trả lời này dành cho các phiên bản Magento 2.2 bên dưới, vì vậy vui lòng ghi chú lại. Theo tiêu chuẩn Magento 2 mới, chúng tôi cũng không thể sử dụng ngay cả đối tượng objectManager. Chúng ta phải sử dụng nhà máy của lớp đối tượng hoặc kho lưu trữ để lấy bất kỳ dữ liệu nào.


Nó là một thực hành tốt để sử dụng nó theo cách này?
enrico69

Có, vì magento không cho phép sử dụng objectManager trực tiếp, nên bạn phải sử dụng cách này!
Ronak Chauhan

Bạn cũng không bao giờ nên sử dụng nó trong các sự kiện (tôi đoán bạn có nghĩa là Người quan sát) và plugin. Bạn nên tiêm các đối tượng mà bạn cần, không phải ObjectManager. Chỉ trong một Nhà máy, bạn mới có thể sử dụng Trình quản lý đối tượng và sau đó bạn thực sự nên tiêm nó thay vì gọi::getInstance()
7ochem

Phải, chỉnh sửa câu trả lời @ 7ochem
Ronak Chauhan

downvote bất kỳ câu trả lời nào không phải là một cách thích hợp, Nếu bạn có kiến ​​thức tốt hơn thì bạn có thể thêm câu trả lời của riêng mình hoặc bạn có thể chỉnh sửa bất kỳ câu trả lời nào khác để có ý tưởng tốt hơn và hữu ích cho người khác. @ 7ochem
Ronak Chauhan

10

Lý do chính khiến các nhà phát triển không khuyến khích sử dụng Trình quản lý đối tượng trực tiếp là việc sử dụng trực tiếp Trình quản lý đối tượng khiến tiện ích mở rộng không thể cài đặt được trong chế độ phát hành được biên dịch.

Vì vậy, nó phá vỡ cho khách hàng của bạn bằng chế độ phát hành, bao gồm tất cả khách hàng trên Magento Cloud.

Có vẻ như một tỷ lệ khá lớn các nhà phát triển (khoảng 75%) không kiểm tra các tiện ích mở rộng của họ để xem liệu chúng có thể được cài đặt trong chế độ phát hành hay không, do đó, không gặp phải các vấn đề do sử dụng ObjectManager không chính xác.

Kể từ năm 2017, Magento Marketplace chạy thử nghiệm biên dịch và cài đặt trên tất cả các tiện ích mở rộng được bán thông qua nó. Nếu tiện ích mở rộng của bạn sử dụng Trình quản lý đối tượng trực tiếp, nó sẽ thất bại trong các thử nghiệm này và bị từ chối khỏi Thị trường cho đến khi bạn giải quyết vấn đề này và tải lại.


2

Bạn có thể thử bằng cách tạo một đối tượng của objectManager và không nên sử dụng objectManager trực tiếp .

Sử dụng một cái gì đó như,

class Example extends \Magento\Framework\View\Element\Template
{
    private $_objectManager;

    public function __construct(
        \Magento\Framework\ObjectManagerInterface $objectmanager
    ){
        $this->_objectManager = $objectmanager;
    }

    public function getExample()
    {
        $customerSession = $this->_objectManager->create("Magento\Customer\Model\Session");
        if ($customerSession->isLoggedIn()) {
            $customerData = $customerSession->getCustomer()->getData();
            /*Your logic*/
        }
    }
}

2
Nếu người quản lý đối tượng là một người độc thân, tại sao điều này sẽ tạo ra sự khác biệt?
domdambrogia
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.