Magento 2 non supporta l'iniezione di dipendenza nei tratti?


8

I tratti funzionano davvero con l'iniezione di dipendenza in Magento? Considera il seguente codice:

Classe di tratto

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 usando il tratto

namespace Frame\Slick\Block;

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

Questo sembra restituire sempre null, sono molto sicuro che tutto sia stato incluso correttamente. Il tratto può davvero supportare l'iniezione di dipendenza?

EDIT: Ad esempio se si fa un di nel costruttore del tratto e lo si assegna a una variabile del tratto e quindi lo si chiama sulla classe che utilizza il tratto, restituirà sempre null. Qualsiasi altra cosa funziona bene.


Solo una domanda ... "testTrait ()" restituisce null o "$ this -> _ slickHelper" è null?
Phoenix128_RiccardoT

$ this -> _ slickHelper restituisce null, altri metodi nel tratto funzionano solo con quelli assegnati alle variabili del tratto non funzionano.
André Ferraz,

1
Buona domanda. Suppongo che Magento usi Reflection per ispezionare gli argomenti del costruttore e questo funziona bene con i tratti: 3v4l.org/jbVTU - ma dovrei dare un'occhiata più da vicino alla generazione del codice per verificarlo.
Fabian Schmengler,

ma perché vuoi usare i tratti? Puoi fare un esempio di vita reale? Forse c'è un modo più semplice per
aggirarlo

@Marius Ho creato questo modulo che funge da dispositivo di scorrimento per blocchi CMS, vendite incrociate, prodotti (di una categoria specifica) e vendite superiori. Ognuna di queste classi di blocchi estende un'altra classe, ad esempio i prodotti estendono Magento \ Catalog \ Block \ Product \ ListProduct. Davvero il motivo per cui sto usando i tratti è perché risolve il "problema" dell'architettura a eredità singola PHP. In questo modo c'è meno ripetizione del codice.
André Ferraz,

Risposte:


2

Ho testato usando tratto e funziona benissimo.

Ecco come appare il mio tratto:

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

Lo uso nel controller in questo modo:

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

E questo è il risultato:

Risultato del test del tratto


0

Lo stavo affrontando da solo. Il post originale è piuttosto vecchio, quindi le cose potrebbero essere diverse ora rispetto a quando è stato pubblicato, tuttavia quello che ho scoperto è che il costruttore DI funziona ma ha un avvertimento piuttosto grande.

Se uso il seguente tratto nel mio codice:

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

e quindi procedere con l'uso di quel tratto in una classe:

<?php

namespace My\Module;

use \My\Module\Util\LoggerTrait;

class Service
{
    use LoggerTrait;

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

L'interfaccia del logger viene iniettata perfettamente e tutto funziona bene. TUTTAVIA, se voglio iniettare le mie classi nella mia classe di servizio usando il metodo del costruttore. Per esempio:

<?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!');
    }
}

In questo caso il metodo di costruzione della mia caratteristica non viene mai chiamato, il che significa che la proprietà $ logger della mia classe non viene mai impostata. Devo ammettere che non ho usato molto i tratti, quindi la mia conoscenza è un po 'limitata, ma la mia ipotesi è che ciò è dovuto al fatto che la mia classe ha scavalcato il metodo di costruzione del mio tratto. Questo è praticamente un punto fermo poiché la maggior parte della base di codice Magento utilizza costruttori per iniettare dipendenze, escludendo in modo affettivo il loro uso nei tratti.

L'unica vera soluzione che posso vedere è utilizzare l'ObjectManager direttamente per iniettare le dipendenze dei tratti:

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

Disclaimer: l'uso di ObjectManager in Magento è generalmente sconsigliato, ma da quello che vedo in questo caso è l'unica vera opzione. Nel mio esempio, se si desidera impostare un'interfaccia logger diversa nella propria classe, è comunque possibile farlo iniettandola nel proprio costruttore e sovrascrivendo la proprietà $ logger delle classi.


Nella tua classe, hai dichiarato 2 __construct, che è uno importato dal tratto e l'altro nella classe stessa. Tuttavia, non è possibile avere 2 metodi con lo stesso nome in una singola classe. Quindi, fondamentalmente nel tuo caso, __constructil tratto viene ignorato dalla __constructclasse stessa.
Rendy Eko Prastiyo,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.