5

Is it possible to DI dependencies in Drupal 8 but initialize it with values from a config object?

For example, SoapClient requires specifying the WSDL when called. I have different WSDL URLs depending on what environment I am using (test/prod etc).

I know you can do this from the services yaml file, but I have not read of a way where you can reference a value or value(s) from a config object.

My ultimate goal is to create a decorator or factory for SoapClient, but I'm not sure which.

edit: updated with code.

An interface:

<?php

namespace Drupal\iana_netforum_auth\Factory;

use SoapClient;

interface SoapClientFactoryInterface {

  /**
   * Create a SoapClient.
   *
   * This method should prepopulate the SoapClient with values from our
   * module configuration so it is ready to use.
   *
   * @return SoapClient
   */
  public function create() : SoapClient;

}

The factory:

<?php

namespace Drupal\iana_netforum_auth\Factory;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\iana_netforum_auth\Exception\NetforumException;
use SoapClient;

class SoapClientFactory implements SoapClientFactoryInterface {

  public function __construct(ConfigFactoryInterface $config_factory) {
    $this->configFactory = $config_factory;
  }

  public function create() : SoapClient {
    $config = $this->configFactory->get('iana_netforum_auth.settings');
    $arguments = [];

    try {
      if ($config->get('mode') == 'live') {
        $endpoint = $config->get('live_endpoint');
      }
      else {
        $endpoint = $config->get('dev_endpoint');
        $arguments = [
          'trace' => $config->get('enable_trace'),
          'exceptions' => $config->get('enable_exceptions')
        ];
      }

      return new SoapClient($endpoint, $arguments);
    }
    catch (SoapFault $error) {
      throw new NetforumException('Invalid WSDL endpoint specified.');
    }
  }

}

The class:

class NetforumClient implements NetforumClientInterface {

  protected $username;
  protected $password;
  protected $token;
  protected $authorizationHeaders;

  /**
   * Drupal\Core\Config\ConfigFactoryInterface definition.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * Drupal\iana_netforum_auth\Factory\SoapClientFactoryInterface definition.
   *
   * @var \Drupal\iana_netforum_auth\Factory\SoapClientFactoryInterface
   */
  protected $soapClient;

  /**
   * Class constructor.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   An instance of ConfigFactoryInterface.
   * @param \Drupal\iana_netforum_auth\Factory\SoapClientFactoryInterface $soap_client_factory
   *   An instance of SoapClientFactoryInterface.
   */
  public function __construct(ConfigFactoryInterface $config_factory, SoapClientFactoryInterface $soap_client_factory) {
    $this->configFactory = $config_factory;
    $this->soapClient = $soap_client_factory->create();
  }
2
  • 1
    Can you share a bit more of what you're trying to create exactly? You could always inject the config factory, get the config object and pass it to the parent constructor. Or you could make a factory service that creates SoapClient instances that you then use.
    – Berdir
    Jul 17 '17 at 20:40
  • Maybe that's what I'm looking for, could you explain a bit more? I want a soap client ready to go with different config from my config settings when I call my wrapper class (that uses the soap client).
    – Kevin
    Jul 17 '17 at 20:45
5

Not sure which part exactly is unclear. There are lots of examples in core.services.yaml that inject the config.factory service to read a configuration object, for example this:

diff.formatter:
  class: Drupal\Core\Diff\DiffFormatter
  arguments: ['@config.factory']

The constructor for that then looks like this:

/**
 * Creates a DiffFormatter to render diffs in a table.
 *
 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
 *   The config factory.
 */
public function __construct(ConfigFactoryInterface $config_factory) {
  $config = $config_factory->get('system.diff');
  $this->leading_context_lines = $config->get('context.lines_leading');
  $this->trailing_context_lines = $config->get('context.lines_trailing');
}

Once you have the config object, you get the configuration you want and pass it along to the SoapClient constructor.

7
  • It's weird, because when I created a factory for the soap client, the soap client started throwing errors about it not being an object, but a reference to an object when I was using it. Perhaps it is something peculiar about SoapClient that I don't understand yet. The Symfony docs for using a factory in services.yml also threw errors, seems that it needs a key and not a path to a class.
    – Kevin
    Jul 18 '17 at 22:29
  • It sounds a lot like this error, actually: stackoverflow.com/questions/22140917/…
    – Kevin
    Jul 18 '17 at 22:43
  • That's C#, that has nothing to do with PHP ;) I won't be able to help you as long as you don't share the the relevant part of your code
    – Berdir
    Jul 19 '17 at 0:16
  • Hang on... forgot to stash that and had to redo.
    – Kevin
    Jul 19 '17 at 1:20
  • Oddly enough - I rewrite the code and don't get the linked error anymore about "Object reference is not set to an object", but I posted it above. Is this the "right" way of injecting a factory?
    – Kevin
    Jul 19 '17 at 1:29

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.