Magento 2 ne prend pas en charge l'injection de dépendance dans les traits?


8

Les traits fonctionnent-ils réellement avec l'injection de dépendance dans Magento? Considérez le code suivant:

Classe de caractère

namespace Frame\Slick\Block;
use Frame\Slider\Slick\Block\Data as Helper

trait Slick
{
   protected $_slickHelper;
   public function __construct(Helper $slickHelper) 
   {
     $this->_slickHelper = $slickHelper;
   }
}

Classe utilisant le trait

namespace Frame\Slick\Block;

class Product ListProduct implements BlockInterface 
{
   use Slick;
   public function testTrait()
   {
      return $this->_slickHelper->getHelloWorld();
   }
}

Cela semble toujours retourner null, je suis très sûr que tout est correctement inclus. Le trait peut-il vraiment soutenir l'injection de dépendance?

EDIT: Par exemple, si vous faites un di dans le constructeur de trait et l'assignez à une variable de trait et que vous l'appelez ensuite sur la classe qui utilise le trait, il retournera toujours null. Tout le reste fonctionne bien.


Une seule question ... "testTrait ()" renvoie-t-il null ou "$ this -> _ slickHelper" est-il nul?
Phoenix128_RiccardoT

$ this -> _ slickHelper renvoie null, les autres méthodes du trait ne fonctionnent que les di affectées aux variables de trait ne fonctionnent pas.
André Ferraz

1
Bonne question. Je suppose que Magento utilise Reflection pour inspecter les arguments du constructeur et cela fonctionne bien avec les traits: 3v4l.org/jbVTU - mais je devrais regarder de plus près la génération de code pour le vérifier.
Fabian Schmengler

mais pourquoi voulez-vous utiliser des traits? Pouvez-vous donner un exemple concret? Peut-être existe-t-il un moyen plus simple de le contourner
Marius

@Marius J'ai créé ce module qui agit comme un curseur pour les blocs CMS, les ventes croisées, les produits (d'une catégorie spécifique) et les ventes incitatives. Chacune de ces classes de blocs étend une autre classe, par exemple les produits étendent Magento \ Catalog \ Block \ Product \ ListProduct. Vraiment, la raison pour laquelle j'utilise des traits est qu'il résout le "problème" de l'architecture d'héritage unique PHP. De cette façon, il y a moins de répétition de code.
André Ferraz

Réponses:


2

J'ai testé en utilisant trait et cela fonctionne très bien.

Voici à quoi ressemble mon trait:

<?php

namespace ProjectName\ModuleName\Controller\Adminhtml;

use Magento\Backend\App\Action\Context;
use ProjectName\ModuleName\Model\ResourceModel\Distributor\CollectionFactory as DistributorCollectionFactory;

trait DistributorTrait
{
    protected $distributorCollectionFactory;

    public function __construct(
        Context $context,
        DistributorCollectionFactory $distributorCollectionFactory
    )
    {
        parent::__construct($context);

        $this->distributorCollectionFactory = $distributorCollectionFactory;
    }
}

Je l'utilise dans un contrôleur comme celui-ci:

<?php

namespace ProjectName\ModuleName\Controller\Adminhtml\Distributor;

use Magento\Backend\App\Action;
use ProjectName\ModuleName\Controller\Adminhtml\DistributorTrait;

class Index extends Action
{
    use DistributorTrait;

    public function execute()
    {
        dump($this->distributorCollectionFactory->create()->getItems());exit;
    }
}

Et voici le résultat:

Résultat du test de caractère


0

J'étais juste face à ça moi-même. Le message d'origine est assez ancien, donc les choses peuvent être différentes maintenant que lorsqu'il a été publié, mais ce que j'ai trouvé, c'est que le constructeur DI fonctionne, mais il a une mise en garde assez importante.

Si j'utilise le trait suivant dans mon code:

<?php

namespace My\Module\Util;

use Psr\Log\LoggerInterface;

trait LoggerTrait
{
    protected $logger;

    public function __construct(
        LoggerInterface $logger
    ) {
        $this->logger = $logger;
    }

    /**
     * @return Logger
     */
    public function getLogger()
    {
        return $this->logger;
    }

    /**
     * @param Logger $logger
     */
    public function setLogger($logger)
    {
        $this->logger = $logger;
    }
}

puis continuez à utiliser ce trait dans une classe:

<?php

namespace My\Module;

use \My\Module\Util\LoggerTrait;

class Service
{
    use LoggerTrait;

    public function doSomething() {
        $this->getLogger()->log('Something was done!');
    }
}

L'interface de l'enregistreur est parfaitement injectée et tout fonctionne bien. CEPENDANT, si je veux injecter mes propres classes dans ma classe Service en utilisant la méthode constructeur. Par exemple:

<?php

namespace My\Module;

use \My\Module\Util\LoggerTrait;


class Service
{
    use LoggerTrait;

    public function __construct(
         \Some\Other\Class $class
    ) {
        $this->other = $class;
    }


    public function doSomething() {
        $this->getLogger()->log('Something was done!');
    }
}

Dans ce cas, la méthode constructeur de ma caractéristique n'est jamais appelée, ce qui signifie que la propriété $ logger de ma classe n'est jamais définie. Certes, je n'ai pas beaucoup utilisé les traits, donc mes connaissances sont assez limitées, mais je suppose que c'est parce que ma classe a outrepassé la méthode constructeur de mon trait. C'est à peu près un bouchon de show car la plupart de la base de code Magento utilise des constructeurs pour injecter des dépendances, excluant affectivement leur utilisation dans les traits.

La seule vraie solution que je puisse voir est d'utiliser directement l'ObjectManager pour injecter vos dépendances de traits:

<?php

namespace My\Module\Util;

use Psr\Log\LoggerInterface;

trait LoggerTrait
{
    protected $logger;


    /**
     * @return Logger
     */
    public function getLogger()
    {
        if (is_null($this->logger)) {
            $objectManager = \Magento\Framework\App\ObjectManager::getInstance();
            $this->logger = $objectManager->create('Psr\Log\LoggerInterface');
        }
        return $this->logger;
    }

    /**
     * @param Logger $logger
     */
    public function setLogger($logger)
    {
        $this->logger = $logger;
    }
}

Avertissement: L'utilisation d'ObjectManager dans Magento est généralement déconseillée mais d'après ce que je peux voir dans ce cas, c'est la seule véritable option. Dans mon exemple, si vous souhaitez définir une autre interface de journalisation dans votre classe, vous pouvez toujours le faire en l'injectant dans votre constructeur et en remplaçant la propriété classes $ logger.


Dans votre classe, vous avez déclaré 2 __construct, qui est l'un importé de trait et l'autre dans la classe elle-même. Cependant, vous ne pouvez pas avoir 2 méthodes avec le même nom dans une seule classe. Donc, fondamentalement, dans votre cas, __constructle trait est remplacé par __constructdans la classe elle-même.
Rendy Eko Prastiyo
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.