11.4 依赖注入和插件
插件
插件是添加依赖注入最复杂的组件。很多插件不需要依赖注入,有时找例子来拷贝都具有挑战性。多数插件继承了使用接口的类。例如 Blocks,继承了 BlockBase
,BlockBase
实现了 BlockPluginInterface 。
依赖注入应该添加在个别插件级,而不是基类级别。这意味着我们能够向任何插件添加依赖注入,创建新插件时,也不总是需要使用依赖注入。
实现 ContainerFactoryPluginInterface
插件使用依赖注入的关键是实现 ContainerFactoryPluginInterface 。当插件被创建时,代码首先检查插件是否实现了这个接口。如果实现,使用 create()
和 __construct()
模式,如果未实现,则只使用 __construct() 模式。
<?php
/**
* @file
* Contains \Drupal\Core\Plugin\Factory\ContainerFactory.
*/
namespace Drupal\Core\Plugin\Factory;
use Drupal\Component\Plugin\Factory\DefaultFactory;
/**
* Plugin factory which passes a container to a create method.
*/
class ContainerFactory extends DefaultFactory {
/**
* {@inheritdoc}
*/
public function createInstance($plugin_id, array $configuration = array()) {
$plugin_definition = $this->discovery->getDefinition($plugin_id);
$plugin_class = static::getPluginClass($plugin_id, $plugin_definition, $this->interface);
// If the plugin provides a factory method, pass the container to it.
if (is_subclass_of($plugin_class, 'Drupal\Core\Plugin\ContainerFactoryPluginInterface')) {
return $plugin_class::create(\Drupal::getContainer(), $configuration, $plugin_id, $plugin_definition);
}
// Otherwise, create the plugin directly.
return new $plugin_class($configuration, $plugin_id, $plugin_definition);
}
}
创建一个区块
现在我们将创建一个区块,功能类似于我们的控制器。我们通过实现 ContainerFactoryPluginInterface 添加依赖注入。使用我们之前的 create()
和 __construct()
模式。因为插件带有额外的参数,我们也把这些参数传递给上述两个方法。__construct() 也调用了父类构造器,结果父类构造器提供的功能被保留。
<?php
/**
* @file
* Contains \Drupal\di_example\Plugin\Block\DIExample.
*/
namespace Drupal\di_example\Plugin\Block;
use Drupal\Core\Block\BlockBase;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\di_example\DITalk;
/**
* Provides a block to that shows a conversation about mood.
*
* @Block(
* id = "di_example_conversation_mood",
* admin_label = @Translation("DI Example: Conversation about mood")
* )
*/
class DIExample extends BlockBase implements ContainerFactoryPluginInterface {
/**
* @var $dITalk \Drupal\di_example\DITalk
*/
protected $dITalk;
/**
* @param array $configuration
* @param string $plugin_id
* @param mixed $plugin_definition
* @param \Drupal\di_example\DITalk $DITalk
*/
public function __construct(array $configuration, $plugin_id, $plugin_definition, DITalk $DITalk) {
// Call parent construct method.
parent::__construct($configuration, $plugin_id, $plugin_definition);
// Store our dependency.
$this->dITalk = $DITalk;
}
/**
* @param \Symfony\Component\DependencyInjection\ContainerInterface $container
* @param array $configuration
* @param string $plugin_id
* @param mixed $plugin_definition
* @return static
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
return new static(
$configuration,
$plugin_id,
$plugin_definition,
$container->get('di_example.talk')
);
}
/**
* {@inheritdoc}
*/
public function build() {
// We use the injected service to get the message.
$message = $this->dITalk->getResponseToMood();
// We return a render array of the message.
return [
'#type' => 'markup',
'#markup' => '<p>' . $this->t($message) . '</p>',
];
}
}
当你不能使用依赖注入时
在一个静态上下文里你不能使用依赖注入。这意味着被调用的方法不在类里,或者你正在使用一个类的静态方法。因为我们没有一个实例所以从来也不会调用 __construct() 方法,也不能使用 $this,因为它不会指向任何东西。
静态上下文多数在 .module 文件内。例如我们在 module 内可以这样使用上面的服务。
di_example.module :
<?php
/**
* Implements hook_entity_load().
*
* @param $entities
* @param $entity_type
*/
function di_example_entity_load($entities, $entity_type) {
/**
* Because we are not in a class, we cannot use dependency injection.
*
* @var $mood_ring_service \Drupal\di_example\DIMoodRing
*/
$mood_ring_service = \Drupal::service('di_example.mood_ring');
$mood = $mood_ring_service->getMood();
}