Magento 2: utiliser ou ne pas utiliser ObjectManager directement?


134

Ok, donc hier nous avons eu une grosse discussion avec d’autres membres de la communauté Magento concernant l’ utilisation directe de ObjectManagerin classes / modèles .

Je connais déjà les raisons pour lesquelles nous ne devrions pas utiliser directement ObjectManager, citant Alan Kent :

Il y a plusieurs raisons. Le code fonctionnera, mais il est recommandé de ne pas référencer directement la classe ObjectManager.

  • Parce que nous le disons! ;-) (mieux exprimé car un code cohérent est un bon code)
  • Le code pourrait être utilisé avec un cadre d'injection de dépendance différent à l'avenir
  • Le test est plus simple : vous passez des arguments fictifs pour la classe requise sans devoir fournir d'objet fictif.
  • Les dépendances restent plus claires - il est évident que le code dépend de la liste de constructeurs, plutôt que d'avoir des dépendances cachées au milieu du code
  • Cela encourage les programmeurs à mieux réfléchir aux concepts tels que l'encapsulation et la modularisation - si le constructeur grand, c'est peut-être un signe que le code doit être refactorisé.

D'après ce que j'ai vu dans StackExchange, beaucoup de gens ont tendance à opter pour la solution facile / courte / non recommandée, par exemple quelque chose comme ceci:

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

Au lieu de passer par le processus douloureux mais recommandé de:

  • créer un module
  • déclarer ses préférences
  • injecter des dépendances
  • déclarer une méthode publique

Cependant, et voici le dilemme, les fichiers de base de Magento 2 appellent souvent directement ObjectManager . Vous trouverez un exemple rapide ici: https://github.com/magento/magento2/blob/develop/app/code/Magento/GoogleOptimizer/Block/Adminhtml/Form.php#L57

Donc, voici mes questions:

  • Pourquoi Magento fait-il ce qu'ils nous recommandent de ne pas faire? Cela signifie-t-il qu'il y a des cas où nous devrions utiliser le ObjectManagerlogiciel directement ? Si oui, quels sont ces cas?
  • Quelles sont les conséquences de l'utilisation directe d'ObjectManager ?


3
Lien pertinent: mwop.net/blog/2016-04-26-on-locators.html . Le bit pertinent de ce serait The intent of zend-servicemanager is for use as an Inversion of Control container. It was never intended as a general purpose service locator [...]. Cela s'applique également à M2. Consultez également la There are valid use casessection, qui, là encore, s’applique ici.
Nevermind

3
Il y avait une période de développement de M2 ​​où OM était déjà là, mais tout magento n'était pas encore changé pour utiliser l'injection de constructeur. À ce stade, de nombreuses personnes ont remplacé Mage :: getSingleton () par ObjectManager :: getInstance () -> get (). La plupart de ces usages ont été introduits à cette époque. Plus tard, tous les appels Mage :: getSingleton () ont été remplacés par l'injection de constructeur par un outil, mais l'outil n'a pas reconnu ObjectManager :: getInstance () et n'a donc pas été remplacé par l'injection de constructeur.
Anton Kril

3
Duplication possible de l' instance
Teja Bhagavan Kollepara

3
@TejabhagavanKollepara avez-vous lu les deux questions? Il y a des choses semblables mais loin d'être dupliquées
Raphael au Digital Pianism

Réponses:


98

Vous ne devez pas utiliser directement ObjectManager!

Les exceptions à la règle sont:

  • dans les méthodes magiques comme statiques __wakeup, serializeetc.
  • au cas où vous devriez faire la compatibilité ascendante du constructeur
  • dans la portée globale, comme dans les appareils de test d'intégration.
  • dans la classe qui n'a besoin que pour la création d'objets comme la fabrique, le proxy, etc.

2
Je sais que je ne devrais jamais l'utiliser directement, mais pourquoi Magento le fait-il? ^^
Raphael au Digital Pianism

2
dans votre exemple, la compatibilité descendante
KAndy

Ceux-ci sont-ils toujours marqués comme @deprecated?
Raphael au pianisme numérique


5
oh ouais mon pote je sais que c'est juste déroutant. Ils auraient peut-être dû dire "Ne le faites pas, mais sachez que nous avons probablement laissé des erreurs ici et là";)
Raphael au Pianisme en Digital

53

Pourquoi M2 accède-t-il parfois directement au gestionnaire d’objets lorsque nous le déconseillons?

Réponse brutale: M2 est un portage de M1 - pas une réécriture complète. Donc, ne supposez pas que tout le code M2 ​​est parfaitement porté (malheureusement). Ce n'est pas parce que vous trouvez quelque chose dans la base de code M2 ​​que c'est "la meilleure façon de le faire". Parfois, c’est juste "nous n’avons pas encore pris le temps de le réparer".

Moins brutal: comme d’autres réponses, vous DEVEZ l’utiliser car il n’ya pas d’alternative. D'autres fois, cela pourrait être pour des raisons de compatibilité ascendante. Et le code-cadre a parfois du sens de l'utiliser directement, car c'est un code-cadre. Mais si je devais deviner sans regarder le code, beaucoup devraient vraiment être corrigés, mais la priorité n’a pas été suffisamment élevée pour le faire pour le moment.

Rappelez-vous simplement le bon conseil parental: "Enfants, faites ce que je dis, pas ce que je fais!"


9
excellente citation: Les enfants, fais ce que je dis, pas ce que je fais!
Sivakumar

Ça ne fonctionne pas comme ça kiddo
Ansyori

Existe-t-il un moyen recommandé par Magento 2 d’avoir un problème de dépendance logicielle sans gestionnaire d’objet? J'ai un module avec une dépendance douce sur un autre (il charge une autre classe si le module existe). Je ne peux pas participer à cette classe parce que DI échouera. Je ne peux même pas créer une usine pour cette classe parce que l'usine échouera.
Nathan Merrill

51

Vous ne devriez jamais utiliser \Magento\Framework\App\ObjectManager::getInstance().
Il va à l'encontre du but de l'injection de dépendance. Nous sommes de retour à Mage::getModel().
Le gestionnaire d'objets ne doit être utilisé que dans les usines, puis injecté dans un constructeur.

L'avantage d'utiliser ceci est moins de code à écrire. Mais cela ne le rend pas OK.
Le fait que cela soit toujours utilisé dans le noyau, c'est parce que ça n'a pas encore été refactorisé. J'espère que ça va être.


5
Nous sommes donc tous les deux d’accord sur le fait que le code Magento est mal fait, non?
Raphael au pianisme numérique

11
droite. ils ont tort :).
Marius

Je ne pense pas qu'ils utilisent mal. Ils l'utilisent lorsque cela est nécessaire: lorsqu'une résolution dynamique est nécessaire (plug-ins, en particulier) et lorsque BC est maintenu sur des méthodes immédiatement obsolètes.
Nevermind

2
@nevvermind À l'aide d'une usine. Vous utilisez di.xmlpour créer une carte clé => nom de classe, injecter cette carte dans le constructeur de la fabrique et utiliser la fabrique pour instancier la classe via objectmanager
Marius

2
@nevvermind Mais l'opinion d'un employé de Magento est supérieure à votre opinion. Vous avez une réponse ci - dessus de kandy qui indique dans la lettre gras « vous ne devriez pas utiliser le gestionnaire d'objets directement »: magento.stackexchange.com/a/117103/146 Je suppose que ce type efface du brouillard sur la question.
Marius

22

Pourquoi Magento fait-il ce qu'ils nous recommandent de ne pas faire? Cela signifie-t-il qu'il y a des cas où nous devrions utiliser ObjectManager directement? Si oui, quels sont ces cas?

Sans connaître toute l'histoire, voici ce que je suppose:

Au cours du développement de l'équipe M2 Magento à un moment a couru un script automatisé qui a remplacé les occurrences de Mage:getModel(), Mage::getSingleton(), $layout->createBlock(), etc. utiliser le ObjectManager.

Un refactoring ultérieur aurait dû résoudre ce problème pour utiliser à la place l'injection de dépendance appropriée, mais le temps et les ressources étaient insuffisants pour convertir toutes les occurrences.

De plus, l'équipe de Magento semble récemment utiliser cela comme un mécanisme d'évasion. Au lieu de casser une implémentation existante (en changeant le constructeur), ils cachent simplement la nouvelle dépendance via le gestionnaire d’objets. Je ne peux pas dire que je suis d'accord avec cette approche - écrire un code pire pour éviter une pause en Colombie-Britannique.

Quelles sont les conséquences directes de l'utilisation directe d'ObjectManager?

Je pense que votre question comprend déjà suffisamment de raisons. Généralement, cela crée une dépendance cachée. En d'autres termes, la dépendance se trouve dans les détails de l'implémentation et n'est pas visible uniquement par le constructeur.


C'est ironique, car s'ils l'avaient fait correctement avant de rendre publique, la Colombie-Britannique n'aurait pas été un problème
Robbie Averill,

12

Ne doit pas utiliser directement le gestionnaire d'objets!

Par exemple:

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

De même, si vous travaillez avec des observateurs d'événements ou des plug-ins, vous ne devez jamais l'utiliser directement.

Vous pouvez l'utiliser dans les usines, sauf que vous devez d'abord injecter le gestionnaire d'objets dans le constructeur, puis utiliser son objet dans votre méthode.

Préférable d'utiliser:

1) déclarer un objet privé:

private $_objectManager;

2) injecter dans le constructeur et initialiser:

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

3) utiliser dans une méthode:

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

Cette réponse concerne les versions de Magento 2.2 ci-dessous, prenez donc une note. Conformément aux nouvelles normes Magento 2, nous ne pouvons pas utiliser même une instance d’objectManager. Nous devons utiliser la fabrique de la classe d'objet ou du référentiel pour obtenir des données.


Est-ce une bonne pratique de l'utiliser de cette façon?
enrico69

Oui, car magento ne permet pas d’utiliser Direct ObjectManager, vous devez donc utiliser cette méthode!
Ronak Chauhan

Vous devriez également ne jamais l'utiliser dans des événements (je suppose que vous voulez dire des observateurs) et des plugins. Vous devez injecter les objets dont vous avez besoin, pas le gestionnaire ObjectManager. Seulement dans une usine, vous pouvez utiliser ObjectManager, puis vous devez l'injecter au lieu d'appeler::getInstance()
7ochem

A droite, modifiez la réponse @ 7ochem
Ronak Chauhan le

réduire toute réponse n’est pas un moyen approprié. Si vous avez une meilleure connaissance, vous pouvez ajouter votre propre réponse ou vous pouvez modifier la réponse de toute autre personne pour obtenir une idée plus précise et plus utile pour les autres. @ 7ochem
Ronak Chauhan

10

La principale raison pour laquelle il est fortement déconseillé aux développeurs d'utiliser directement le gestionnaire d'objets est que l'utilisation directe de ce dernier empêche l'installation de l'extension en mode de publication compilée.

Cela rompt donc pour vos clients utilisant le mode de publication, y compris tous les clients de Magento Cloud.

Il semble qu'une proportion assez importante de développeurs (environ 75%) ne testent pas leurs extensions pour savoir si elles peuvent être installées en mode de publication. Par conséquent, ne rencontrez pas les problèmes posés par une utilisation incorrecte de ObjectManager.

À partir de 2017, le marché Magento exécute un test de compilation et d'installation sur toutes les extensions vendues par son intermédiaire. Si votre extension utilise directement le gestionnaire d'objets, elle échouera à ces tests et sera rejetée de la place de marché jusqu'à ce que vous résolviez ce problème et effectuez un nouveau chargement.


2

Vous pouvez essayer en créant un objet de objectManager et ne pas utiliser directement objectManager .

Utilisez quelque chose comme

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
Si le gestionnaire d'objets est un singleton, pourquoi cela ferait-il une différence?
Domdambrogia
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.