4

I am relatively fresh to the DI party and am struggling to get my head around how exactly to use Dependency Injection. I understand that I can pass requirements as part of a service, but what about in my own class?

Say I have the below class where I want the language manager service to be injected:

class Foo implements ContainerInjectionInterface {

  protected $languageManager; 

  public function __construct(LanguageManagerInterface $languageManager) {
    $this->languageManager = $languageManager;
  }

  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('language_manager')
    );
  }
}

When I call new Foo() I am required to pass an instance of LanguageManagerInterface. That makes sense in terms of a normal constructor method, but that would require me to instantiate the class like the below:

$languageManager = \Drupal::service('language_manager');
$foo = new Foo($languageManager)

That just doesn't feel right to me.

Is there something I am missing, or would this class always have to be a service to take advantage of dependency injection?

1
2

A class implementing ContainerInjectionInterface is instantiated by calling the factory method create().

See Drupal\Core\DependencyInjection\ClassResolver

/**
 * Implements the class resolver interface supporting class names and services.
 */
class ClassResolver implements ClassResolverInterface, ContainerAwareInterface {
  use DependencySerializationTrait;
  use ContainerAwareTrait;

  /**
   * {@inheritdoc}
   */
  public function getInstanceFromDefinition($definition) {
    if ($this->container->has($definition)) {
      $instance = $this->container->get($definition);
    }
    else {
      if (!class_exists($definition)) {
        throw new \InvalidArgumentException(sprintf('Class "%s" does not exist.', $definition));
      }

      if (is_subclass_of($definition, 'Drupal\Core\DependencyInjection\ContainerInjectionInterface')) {
        $instance = $definition::create($this->container);
      }
      else {
        $instance = new $definition();
      }
    }

    if ($instance instanceof ContainerAwareInterface) {
      $instance->setContainer($this->container);
    }

    return $instance;
  }

}
2
  • Thanks @4k4. So you are saying I should do $foo = Foo::create()? When I do that with the example above I need to pass a ContianerInterface. If thats right, should this be obtained from \Drupal::getContainer() or is there a better way? Aug 7 '19 at 8:00
  • 1
    I don't know in which context you use the class. If it is a controller then it gets instantiated by the code above automatically. If you want to instantiate Foo in custom OOP code then inject the class resolver or procedural use $foo = \Drupal::classResolver(Foo::class);
    – 4k4
    Aug 7 '19 at 8:07
1

You can't use dependency injection in your custom class without defining it as a service.

The question has been answered in more detail here: Dependency injection in a custom class

1

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.