原文地址:
https://docs.acquia.com/articles/drupal-8-dependency-injection-and-plugins
插件
插件是添加依赖注入最复杂的组件。很多插件不需要依赖注入,有时找例子来拷贝都具有挑战性。多数插件继承了使用接口的类。例如 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(); }