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классом.
— Рэнди Эко Прастийо
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.