Magento 2 unterstützt die Abhängigkeitsinjektion in Merkmalen nicht?

8

Funktionieren Merkmale tatsächlich mit der Abhängigkeitsinjektion in Magento? Betrachten Sie den folgenden Code:

Merkmalsklasse

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

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

Klasse mit dem Merkmal

namespace Frame\Slick\Block;

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

Dies scheint immer null zurückzugeben, ich bin mir sehr sicher, dass alles richtig aufgenommen wird. Kann das Merkmal die Abhängigkeitsinjektion wirklich unterstützen?

BEARBEITEN: Wenn Sie beispielsweise im Trait-Konstruktor ein di ausführen und es einer Trait-Variablen zuweisen und es dann für die Klasse aufrufen, die das Trait verwendet, wird immer null zurückgegeben. Alles andere funktioniert gut.

André Ferraz
quelle
Nur eine Frage ... gibt "testTrait ()" null zurück oder "$ this -> _ slickHelper" ist null?
Phoenix128_RiccardoT
$ this -> _ slickHelper gibt null zurück, andere Methoden im Merkmal funktionieren nur, wenn es den Merkmalvariablen zugewiesen ist, funktionieren sie nicht.
André Ferraz
1
Gute Frage. Ich gehe davon aus, dass Magento Reflection verwendet, um die Konstruktorargumente zu überprüfen, und dies funktioniert gut mit Merkmalen: 3v4l.org/jbVTU - aber ich müsste mir die Codegenerierung genauer ansehen, um sie zu überprüfen.
Fabian Schmengler
aber warum willst du eigenschaften verwenden? Können Sie ein Beispiel aus dem wirklichen Leben geben? Vielleicht gibt es einen einfacheren Weg
Marius
@Marius Ich habe dieses Modul erstellt, das als Schieberegler für CMS-Blöcke, Cross-Sells, Produkte (einer bestimmten Kategorie) und Up-Sells fungiert. Jede dieser Blockklassen erweitert eine andere Klasse, z. B. Produkte erweitern Magento \ Catalog \ Block \ Product \ ListProduct. Der Grund, warum ich Merkmale verwende, ist, dass es das "Problem" der PHP-Einzelvererbungsarchitektur löst. Auf diese Weise wird weniger Code wiederholt.
André Ferraz

Antworten:

2

Ich habe mit Merkmal getestet und es funktioniert gut.

So sieht mein Merkmal aus:

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

Ich benutze es in Controller wie folgt:

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

Und hier ist das Ergebnis:

Ergebnis des Merkmalstests

Rendy Eko Prastiyo
quelle
0

Ich habe mich nur selbst damit konfrontiert. Der ursprüngliche Beitrag ist ziemlich alt, daher können die Dinge jetzt anders sein als zu dem Zeitpunkt, als er veröffentlicht wurde. Ich habe jedoch festgestellt, dass der Konstruktor DI zwar funktioniert, aber eine ziemlich große Einschränkung aufweist.

Wenn ich das folgende Merkmal in meinem Code verwende:

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

und fahren Sie dann fort, dieses Merkmal in einer Klasse zu verwenden:

<?php

namespace My\Module;

use \My\Module\Util\LoggerTrait;

class Service
{
    use LoggerTrait;

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

Die Logger-Schnittstelle ist perfekt injiziert und alles funktioniert einwandfrei. JEDOCH, wenn ich meine eigenen Klassen mit der Konstruktormethode in meine Service-Klasse einfügen möchte. Z.B:

<?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 diesem Fall wird die Konstruktormethode meines Merkmals niemals aufgerufen, was bedeutet, dass die Eigenschaft $ logger meiner Klasse niemals festgelegt wird. Zugegeben, ich habe nicht viel Merkmale verwendet, so dass mein Wissen etwas begrenzt ist, aber ich gehe davon aus, dass dies daran liegt, dass meine Klasse die Konstruktormethode meines Merkmals überschrieben hat. Dies ist so ziemlich ein Show-Stopper, da der Großteil der Magento-Codebasis Konstruktoren verwendet, um Abhängigkeiten zu injizieren, wodurch deren Verwendung in Merkmalen effektiv ausgeschlossen wird.

Die einzige wirkliche Lösung, die ich dafür sehen kann, besteht darin, den ObjectManager direkt zu verwenden, um Ihre Merkmalsabhängigkeiten zu injizieren:

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

Haftungsausschluss: Von der Verwendung von ObjectManager in Magento wird im Allgemeinen abgeraten, aber soweit ich in diesem Fall sehen kann, ist dies die einzige echte Option. Wenn Sie in meinem Beispiel eine andere Logger-Schnittstelle in Ihrer Klasse festlegen möchten, können Sie dies dennoch tun, indem Sie sie in Ihren Konstruktor einfügen und die Eigenschaft class $ logger überschreiben.

Andrew Kett
quelle
In Ihrer Klasse haben Sie 2 deklariert __construct, eine aus dem Merkmal importierte und die andere in der Klasse selbst. Sie können jedoch nicht zwei Methoden mit demselben Namen in einer Klasse haben. Im Grunde genommen wird in Ihrem Fall __constructdas Merkmal __constructin der Klasse selbst überschrieben .
Rendy Eko Prastiyo