Magento 2 не поддерживает внедрение зависимостей в чертах?

8

Работают ли черты с внедрением зависимостей в Magento? Рассмотрим следующий код:

Черта Класс

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

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

Класс, использующий черту

namespace Frame\Slick\Block;

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

Кажется, это всегда возвращает ноль, я уверен, что все включено правильно. Может ли черта действительно поддерживать внедрение зависимостей?

РЕДАКТИРОВАТЬ: Например, если вы сделаете di в конструкторе признака и присвоите его переменной признака, а затем вызовете его в классе, который использует признак, он всегда будет возвращать ноль. Все остальное работает нормально.

Андре Феррас
источник
Только один вопрос ... "testTrait ()" возвращает ноль или "$ this -> _ slickHelper" равно нулю?
Phoenix128_RiccardoT
$ this -> _ slickHelper возвращает null, другие методы в признаке работают только с теми, которые назначены переменным признака, не работают.
Андре Феррас,
1
Хороший вопрос. Я предполагаю, что Magento использует Reflection для проверки аргументов конструктора, и это хорошо работает с чертами: 3v4l.org/jbVTU - но мне нужно было бы поближе взглянуть на генерацию кода, чтобы проверить это.
Фабиан Шменглер
но почему вы хотите использовать черты? Можете ли вы привести пример из жизни? Может быть, есть более простой способ обойти это
Мариус
@Marus Я создал этот модуль, который действует как слайдер для блоков CMS, перекрестных продаж, продуктов (определенной категории) и дополнительных продаж. Каждый из этих классов блоков расширяет другой класс, например, продукты расширяют Magento \ Catalogue \ Block \ Product \ ListProduct. На самом деле причина, по которой я использую признаки, заключается в том, что она решает «проблему» архитектуры единого наследования PHP. Таким образом, меньше повторений кода.
Андре Ферраз

Ответы:

2

Я проверил, используя черту, и она отлично работает.

Вот как выглядит моя черта:

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

Я использую это в контроллере, как это:

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

И вот результат:

Trait Test Result

Ренди Эко Прастийо
источник
0

Я просто столкнулся с этим сам. Исходное сообщение довольно старое, так что теперь все может отличаться от того, когда оно было опубликовано, однако я обнаружил, что конструктор DI работает, но у него довольно большое предостережение.

Если я использую следующую черту в моем коде:

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

а затем перейдите к использованию этой черты в классе:

<?php

namespace My\Module;

use \My\Module\Util\LoggerTrait;

class Service
{
    use LoggerTrait;

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

Интерфейс логгера вводится отлично и все работает отлично. ОДНАКО, если я хочу внедрить свои собственные классы в мой класс Service, используя метод конструктора. Например:

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

В этом случае метод конструктора моей черты никогда не вызывается, то есть свойство $ logger моего класса никогда не устанавливается. По общему признанию, я не использовал черты, поэтому мои знания ограничены, но я предполагаю, что это потому, что мой класс переопределил метод конструктора моей черты. Это в значительной степени ограничитель показа, так как большая часть кода Magento использует конструкторы для введения зависимостей, аффективно исключая их использование в чертах.

Единственное реальное решение, которое я вижу, - это использовать ObjectManager напрямую, чтобы ввести зависимости от вас:

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

Отказ от ответственности: использование ObjectManager в Magento, как правило, не рекомендуется, но из того, что я вижу в этом случае, это единственный реальный вариант. В моем примере, если вы хотите установить другой интерфейс логгера в своем классе, вы все равно можете сделать это, внедрив его в конструктор и переопределив свойство классов $ logger.

Эндрю Кетт
источник
В вашем классе вы объявили 2 __construct, один из которых импортирован из черты, а другой в самом классе. Однако вы не можете иметь 2 метода с одинаковым именем в одном классе. Так что в основном в вашем случае __constructэта черта переопределяется самим __constructклассом.
Рэнди Эко Прастийо