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();
   }
}

这似乎总是返回null,非常确定是否正确包含了所有内容。特质真的可以支持依赖注入吗?

编辑:例如,如果您在trait构造函数中执行di并将其分配给trait变量,然后在使用trait的类上对其进行调用,则它将始终返回null。其他都可以。


只是一个问题...“ testTrait()”返回null还是“ $ this-> _ slickHelper”为null?
Phoenix128_RiccardoT

$ this-> _ slickHelper返回null,在trait中的其他方法仅对分配给trait变量的di有效。
安德烈·费拉斯

1
好问题。我想,Magento使用Reflection来检查构造函数参数,并且可以很好地与以下特征配合使用:3v4l.org/jbVTU-但我将不得不仔细研究一下代码生成以进行验证。
Fabian Schmengler,

但是为什么要使用特征?你能举一个真实的例子吗?也许有一种更简单的解决方法
Marius

@Marius我创建了这个模块,用作CMS块,交叉销售,产品(特定类别)和向上销售的滑块。这些块类中的每个块类都扩展了另一个类,例如产品扩展了Magento \ Catalog \ Block \ Product \ ListProduct。确实,我使用traits的原因是因为它解决了PHP单一继承体系结构的“问题”。这样,代码重复较少。
安德烈·费拉兹

Answers:


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

结果如下:

特质测试结果


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属性。诚然,我没有太多使用traits,所以我的知识是有限的,但是我的假设是,这是因为我的课程已覆盖了我的trait的构造方法。这几乎是一个展示止步器,因为大多数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;
    }
}

免责声明:通常不建议在Magento中使用ObjectManager,但是从我的情况下,我可以看到它是唯一的实际选择。在我的示例中,如果您想在类中设置其他记录器接口,则仍然可以通过将其注入构造函数中并覆盖类$ logger属性来实现。


在您的班级中,您声明了2 __construct,这是一个从trait导入的,另一个在类本身中。但是,单个类中不能有两个具有相同名称的方法。所以基本上在您的情况下,__construct特质__construct在类本身中被覆盖。
Rendy Eko Prastiyo
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.