6

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
    Commented Jul 17, 2017 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
    Commented Jul 17, 2017 at 20:45

1 Answer 1

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
    Commented Jul 18, 2017 at 22:29
  • It sounds a lot like this error, actually: stackoverflow.com/questions/22140917/…
    – Kevin
    Commented Jul 18, 2017 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
    Commented Jul 19, 2017 at 0:16
  • Hang on... forgot to stash that and had to redo.
    – Kevin
    Commented Jul 19, 2017 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
    Commented Jul 19, 2017 at 1:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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