11.4 依赖注入和插件

插件

插件是添加依赖注入最复杂的组件。很多插件不需要依赖注入,有时找例子来拷贝都具有挑战性。多数插件继承了使用接口的类。例如 Blocks,继承了 BlockBaseBlockBase 实现了 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();
}

 

本书共39小节。


评论 (0)