如何在 Yii2 配置中注入 class 依赖?

how to inject class dependency in Yii2 configuration?

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

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

这个 scraper class 有一个 public 属性 $_client 这是一个 Goutte\Client class。

我尝试使用下面的方式来设置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'
        ],
   ],
   //... 
]

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

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 class 中实现。最终,这个实现执行 Yii::configure:

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

    return $object;
}

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

guide itself: The property targets of the class yii\log\Dispatcher can be initialized with a class names or an objects. To make it working as one expects the init方法中发现另一种方法被覆盖:

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

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

这允许 configuration/initialization 这样的日志组件:

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

注意:这里的targets是一个数组。但它也可以用一个 class/object 来完成。

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

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

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