【问题标题】:how to inject class dependency in Yii2 configuration?如何在 Yii2 配置中注入类依赖项?
【发布时间】:2016-02-24 08:58:24
【问题描述】:

我正在学习 Yii2。这是我没有用谷歌搜索答案的一种情况。

我在config/console.php$config['components']数组中注册了一个名为scraper的组件,

这个scraper 类有一个公共属性$_client,它是一个Goutte\Client 类。

我尝试使用下面的方式设置scraper组件,但是不行,Yii2没有将$_client实例化为Goutte\Client对象。

$config = [
   'scraper' => [
        'class' => 'app\models\Scraper',
        '_pageSize' => 10,
        '_client' =>  [  //not working. can not instantiate this property as an object
            'class' => 'Goutte\Client'
        ],
   ],
   //... 
]

问题:在配置中注入依赖的工作方式是什么?

【问题讨论】:

  • Scraper 是从 Model 派生的吗? _client 是公共成员吗? Goutte\Client 是完全限定的类名(不是 app\goutte\Client)?而Goutte\Client构造函数没有参数?
  • Scraper 派生自Component$_client 是公共成员。当我在 Scraper 类的 init() 函数中初始化 $_client 时,而所有其他公共原始类型属性都在此配置文件中设置,它工作正常。如果我在 Scraper 函数中 var_dump($this->_client),它显示 $this-_client 是 array(1) { ["class"]=> string(13) "Goutte\Client" } 的数组。
  • Goutte\Client 是完整的类名,不需要参数。我可以在 Scraper 类中通过use Goutte\Client 导入这个类。
  • 能否提供更多代码(类、命名空间、命名空间用法、配置...),好吗?在 $config 中,我看不到 components 键。我对配置不是很有经验,所以不确定我是否可以帮助你。
  • @robsch,是的,我跳过了这段代码 sn-p 的 components 部分,原因是,除了这个 $_client 属性之外,其他所有属性都被实例化了,所以我只想突出这部分。并感谢您尝试帮助我:)

标签: dependency-injection configuration yii2 config


【解决方案1】:

Yii2 不会实例化超出配置数组第一级的对象。换句话说,scraper 将被实例化为一个对象,但它的属性 _client 将被实例化为一个数组 ['class' => 'Goutte\Client']

你应该自己实现这个逻辑:

class Service extends Component
{
    private $_client = null;

    public $clientClass;

    public function getClient()
    {
        if (null !== $this->_client) {
            return $this->_client;
        }

        $this->_client = new $clientClass;

        return $this->_client;
    }
}

或者,你可以将Goutte\Client注册为一个单独的组件,然后Yii会正确地实例化它。

更新: 为了澄清,从配置中实例化对象是使用yii\base\Configurable 接口完成的,该接口在yii\base\Object 类中实现。最终,这个实现会执行Yii::configure

public static function configure($object, $properties)
{
    foreach ($properties as $name => $value) {
        $object->$name = $value;
    }

    return $object;
}

如您所见,所有属性都将被分配各自的值,因此_client 将成为一个数组,而不是一个对象。

【讨论】:

  • 这是否记录在任何地方?
  • 我已经添加了一些说明。
【解决方案2】:

guide本身中找到了另一种方法:yii\log\Dispatcher类的属性targets可以用类名或对象初始化。为了使其按预期工作,init 方法被覆盖:

/**
 * {@inheritdoc}
 */
public function init()
{
    parent::init();

    foreach ($this->targets as $name => $target) {
        if (!$target instanceof Target) {
            $this->targets[$name] = Yii::createObject($target);
        }
    }
}

这允许像这样配置/初始化日志组件:

'log' => [
    'class' => 'yii\log\Dispatcher',
    'targets' => [
        [
            'class' => 'yii\log\FileTarget',
        ],
    ],
],

注意:这里的目标是一个数组。但也可以使用单个类/对象来完成。

所以在你的情况下,这应该是一个解决方案:

namespace app\models;

class Scraper extends ActiveRecord // or extends from anything that actually implements yii\base\Configurable
{
    public $_client;

    /**
     * {@inheritdoc}
     */
    public function init()
    {
        parent::init();

        if (!$this->_client instanceof Goutte\Client) {
            $this->_client = Yii::createObject($this->_client);
        }
    }
}

顺便说一句:变量名中的下划线前缀通常用于私有属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-05-19
    • 1970-01-01
    • 1970-01-01
    • 2021-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多