Magento 2: untuk menggunakan atau tidak menggunakan ObjectManager secara langsung?

134

Ok, jadi kemarin kami melakukan pembicaraan besar dengan orang lain dari komunitas Magento mengenai penggunaan langsung ObjectManagerdi kelas / templat .

Saya sudah mengetahui alasan mengapa kita tidak boleh menggunakan ObjectManager secara langsung, mengutip Alan Kent :

Ada beberapa alasan. Kode akan berfungsi, tetapi merupakan praktik terbaik untuk tidak merujuk kelas ObjectManager secara langsung.

  • Karena kami bilang begitu! ;-) (lebih baik dinyatakan sebagai kode konsisten adalah kode yang baik)
  • Kode ini dapat digunakan dengan kerangka kerja injeksi ketergantungan yang berbeda di masa mendatang
  • Pengujian lebih mudah - Anda memberikan argumen tiruan untuk kelas yang diperlukan, tanpa harus menyediakan ObjectManager tiruan
  • Itu membuat dependensi lebih jelas - jelas apa kode tergantung melalui daftar konstruktor, daripada memiliki dependensi tersembunyi di tengah-tengah kode
  • Ini mendorong programmer untuk berpikir tentang konsep-konsep seperti enkapsulasi dan modularisasi yang lebih baik - jika konstruktor menjadi besar, mungkin itu adalah tanda bahwa kode perlu refactoring

Dari apa yang saya lihat di StackExchange, banyak orang cenderung mencari solusi mudah / pendek / tidak direkomendasikan, misalnya sesuatu seperti ini:

<?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);

Alih-alih melalui proses yang menyakitkan tetapi direkomendasikan :

  • membuat modul
  • mendeklarasikan preferensi
  • menyuntikkan dependensi
  • mendeklarasikan metode publik

Namun, dan inilah dilema, file inti Magento 2 sering memanggil ObjectManager secara langsung . Contoh cepat dapat ditemukan di sini: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

Jadi inilah pertanyaanku:

  • Mengapa Magento melakukan apa yang mereka sarankan agar kita tidak lakukan? Apakah itu berarti ada beberapa kasus di mana kita harus menggunakan ObjectManagersecara langsung ? Jika demikian, apa sajakah kasus-kasus itu?
  • Apa konsekuensi menggunakan ObjectManager secara langsung ?
Raphael di Digital Pianism
sumber
3
Tautan yang relevan: mwop.net/blog/2016-04-26-on-locators.html . Bit yang relevan adalah The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. Yang berlaku untuk M2 juga. Periksa juga There are valid use casesbagian yang juga berlaku di sini.
nevvermind
3
Ada beberapa periode pengembangan M2 ketika OM sudah ada di sana, tetapi seluruh magento belum berubah untuk menggunakan injeksi konstruktor. Pada saat itu banyak orang mengganti Mage :: getSingleton () dengan ObjectManager :: getInstance () -> get (). Sebagian besar penggunaan tersebut diperkenalkan pada periode itu. Kemudian semua panggilan Mage :: getSingleton () diganti dengan injeksi konstruktor oleh alat, tetapi alat tidak mengenali ObjectManager :: getInstance (), jadi itu tidak menggantikannya dengan injeksi konstruktor.
Anton Kril
3
Kemungkinan duplikat dari contoh pembantu Magento 2
Teja Bhagavan Kollepara
3
@TejabhagavanKollepara apakah Anda membaca kedua pertanyaan? Ada yang serupa tetapi jauh dari duplikat satu sama lain
Raphael di Digital Pianism

Jawaban:

98

Anda seharusnya tidak menggunakan ObjectManager secara langsung!

Pengecualian dari aturan tersebut adalah:

  • dalam metode sihir statis seperti __wakeup, serialize, dll
  • dalam kasus Anda harus membuat kompatibilitas mundur konstruktor
  • dalam lingkup global, seperti dalam perlengkapan uji integrasi.
  • di kelas yang hanya perlu untuk membuat objek seperti pabrik, proksi, dll
KAndy
sumber
2
Saya tahu saya seharusnya tidak pernah menggunakannya secara langsung tetapi mengapa Magento melakukannya? ^^
Raphael di Digital Pianism
2
dalam contoh Anda adalah untuk kompatibilitas ke belakang
KAndy
Apakah itu selalu ditandai sebagai @deprecated?
Raphael di Digital Pianism
1
Bagaimana dengan yang ini: github.com/magento/magento2/blob/develop/app/code/Magento/… ?
Raphael di Digital Pianism
5
oh ya sobat aku tahu itu hanya membingungkan. Mungkin mereka seharusnya mengatakan "jangan lakukan itu tetapi sadarilah bahwa kita mungkin meninggalkan beberapa kesalahan di sana-sini";)
Raphael di Digital Pianism
53

Jadi mengapa M2 terkadang mengakses manajer objek secara langsung saat kami merekomendasikannya?

Jawaban brutal: M2 adalah porta M1 - bukan penulisan ulang lengkap. Jadi jangan berasumsi bahwa semua kode M2 ​​porting sempurna (sayangnya). Hanya karena Anda menemukan sesuatu dalam basis kode M2, itu tidak berarti "itu cara terbaik untuk melakukannya". Terkadang itu hanya "kita belum sempat memperbaikinya".

Kurang brutal: Sesuai tanggapan lain, terkadang Anda HARUS menggunakannya karena tidak ada alternatif. Lain waktu mungkin karena alasan kompatibilitas ke belakang. Dan kode kerangka kerja terkadang masuk akal menggunakannya secara langsung, karena itu adalah kode kerangka kerja. Tetapi jika saya harus menebak tanpa melihat kode, banyak yang benar-benar harus diperbaiki tetapi belum memiliki prioritas yang cukup tinggi untuk melakukannya.

Ingat saja nasihat pengasuhan yang baik: "Anak-anak, lakukan apa yang saya katakan, bukan apa yang saya lakukan!"

Alan Kent
sumber
9
kutipan yang sangat baik: Anak-anak, lakukan apa yang saya katakan, bukan apa yang saya lakukan!
sivakumar
Itu bukan cara kerjanya kiddo
Ansyori
Apakah ada cara yang disarankan Magento 2 untuk memiliki masalah ketergantungan lunak tanpa pengelola objek? Saya memiliki modul dengan ketergantungan lunak pada yang lain (memuat kelas lain jika modul ada). Saya tidak bisa DI kelas itu karena DI akan gagal. Saya bahkan tidak bisa DI sebuah Pabrik untuk kelas itu karena pabrik akan gagal DI.
Nathan Merrill
50

Anda seharusnya tidak pernah menggunakan \Magento\Framework\App\ObjectManager::getInstance().
Itu mengalahkan tujuan injeksi ketergantungan. Kami kembali Mage::getModel().
Manajer objek harus digunakan hanya di pabrik dan kemudian disuntikkan ke konstruktor.

Keuntungan menggunakan ini adalah lebih sedikit kode untuk ditulis. Tapi ini tidak membuatnya baik-baik saja.
Fakta bahwa ini masih digunakan dalam inti, adalah karena itu belum di refactored. Saya harap itu akan terjadi.

Marius
sumber
5
Jadi kami berdua sepakat bahwa kode Magento salah, bukan?
Raphael di Digital Pianism
11
Baik. mereka salah :).
Marius
Saya tidak berpikir mereka menggunakan salah. Mereka menggunakannya saat diperlukan: ketika penyelesaian dinamis diperlukan (plugin, khususnya) dan ketika menjaga BC pada metode yang segera ditinggalkan.
nevvermind
2
@nevvermind Menggunakan pabrik. Anda menggunakan di.xmluntuk membuat kunci => peta nama kelas dan menyuntikkan peta itu ke konstruktor pabrik dan menggunakan pabrik untuk instantiate kelas melalui objectmanager
Marius
2
@nevvermind Tapi pendapat karyawan Magento mengungguli pendapat Anda. Anda memiliki jawaban di atas dari KAndy yang menyatakan dalam huruf tebal "Anda tidak boleh menggunakan manajer objek secara langsung": magento.stackexchange.com/a/117103/146 Saya kira hal itu menghapus kabut pada masalah ini.
Marius
22

Mengapa Magento melakukan apa yang mereka rekomendasikan agar kita tidak lakukan? Apakah itu berarti ada beberapa kasus di mana kita harus menggunakan ObjectManager secara langsung? Jika demikian, apa kasusnya?

Tanpa tahu cerita lengkapnya di sini adalah dugaan saya:

Selama pengembangan M2 tim Magento pada tahap tertentu berlari script otomatis yang menggantikan kejadian Mage:getModel(), Mage::getSingleton(), $layout->createBlock(), dll untuk menggunakan ObjectManager.

Kemudian refactoring seharusnya memperbaikinya sebagai gantinya menggunakan injeksi ketergantungan yang tepat tetapi tidak ada cukup waktu / sumber daya untuk mengubah semua kejadian.

Juga tim Magento akhir-akhir ini tampaknya menggunakan ini sebagai mekanisme pelarian. Alih-alih melanggar implementasi yang ada (dengan perlu mengubah konstruktor) mereka hanya menyembunyikan ketergantungan baru melalui ObjectManager. Saya tidak bisa mengatakan saya setuju dengan pendekatan ini - menulis kode yang lebih buruk untuk menghindari jeda SM.

Apa konsekuensi langsung dari penggunaan ObjectManager secara langsung?

Saya pikir pertanyaan Anda sudah mencakup alasan yang cukup. Secara umum ia menciptakan dependensi tersembunyi, dengan kata lain dependensi dalam detail implementasi dan tidak terlihat dari konstruktor saja.

Kristof di Fooman
sumber
Sungguh ironis karena jika dilakukan dengan benar sebelum dirilis ke publik, BC tidak akan menjadi masalah sama sekali
Robbie Averill
12

Seharusnya tidak menggunakan Manajer objek secara langsung!

Misalnya:

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

juga jika Anda bekerja dengan pengamat acara atau plugin, Anda tidak boleh menggunakannya secara langsung.

Anda bisa menggunakannya di Pabrik, tetapi kecuali bahwa Anda harus menyuntikkan Object Manager di Constructor terlebih dahulu maka Anda dapat menggunakan objeknya dalam metode Anda

Lebih disukai menggunakan:

1) mendeklarasikan objek pribadi:

private $_objectManager;

2) menyuntikkan di konstruktor dan menginisialisasi:

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

3) gunakan dalam beberapa metode:

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

Jawaban ini untuk versi Magento 2.2 di bawah ini, jadi harap dicatat. Sesuai standar Magento 2 baru sekarang kita tidak bisa menggunakan instance objectManager juga. Kita harus menggunakan pabrik dari kelas objek atau repositori untuk mendapatkan data apa pun.

Ronak Chauhan
sumber
Apakah ini praktik yang baik untuk menggunakannya dengan cara ini?
enrico69
Ya, karena magento tidak memungkinkan untuk menggunakan objectManager langsung, jadi Anda harus menggunakan cara ini!
Ronak Chauhan
Anda juga tidak boleh menggunakannya dalam acara (saya kira maksud Anda Pengamat) dan plugin. Anda harus menyuntikkan objek yang Anda butuhkan, bukan ObjectManager. Hanya di Pabrik Anda bisa menggunakan ObjectManager dan kemudian Anda memang harus menyuntikkannya alih-alih menelepon::getInstance()
7ochem
Benar, edit jawaban @ 7ochem
Ronak Chauhan
downvote jawaban apa pun bukan cara yang tepat, Jika Anda memiliki pengetahuan yang lebih baik maka Anda dapat menambahkan jawaban Anda sendiri atau Anda dapat mengedit jawaban orang lain untuk mendapatkan ide yang lebih baik dan membantu orang lain. @ 7ochem
Ronak Chauhan
10

Alasan utama bahwa pengembang sangat tidak disarankan untuk menggunakan Object Manager secara langsung adalah bahwa penggunaan langsung dari Object Manager menyebabkan ekstensi tidak dapat diinstal dalam mode rilis terkompilasi.

Jadi, bagi pelanggan Anda rusak menggunakan mode rilis, termasuk semua pelanggan di Magento Cloud.

Sepertinya sebagian besar pengembang (sekitar 75%) tidak menguji ekstensi mereka untuk melihat apakah mereka dapat diinstal dalam mode rilis, jadi jangan mengalami masalah yang ditimbulkan oleh penggunaan ObjectManager yang salah.

Pada 2017, Magento Marketplace menjalankan kompilasi dan menginstal tes pada semua ekstensi yang dijual melaluinya. Jika ekstensi Anda menggunakan Object Manager secara langsung, itu akan gagal tes ini dan ditolak dari Marketplace sampai Anda menyelesaikan masalah ini dan memuat ulang.

Dewi Morgan
sumber
2

Anda dapat mencoba dengan membuat objek objectManager dan tidak boleh menggunakan objectManager secara langsung .

Gunakan sesuatu seperti,

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*/
        }
    }
}
Kazim Noorani
sumber
2
Jika manajer objek adalah singleton, mengapa ini membuat perbedaan?
domdambrogia