2

I need to do a couple of things:

  1. Inject guzzle's httpclient into class A
  2. Inject class A into my service

What I've tried so far:

  1. services:
      my_module.field_fetcher:
        class: Drupal\my_module\ClassB
        arguments: ['@my_module.obj', '@logger.factory', '@config.factory']
    
      my_module.obj:
        class: Drupal\my_module\ClassA
        arguments: ['@http.client']
    
  2. parameters:
      my_module.http_client.config:
        base_uri: 'http://myurl.com/'
    
    services:
      my_module.http_client:
        factory: '@http_client_factory:fromOptions'
        arguments: ['%my_module.http_client.config%']
    
      my_module.obj:
        class: Drupal\my_module\ClassA
        arguments: ['@my_module.http_client']
    
      my_module.field_fetcher:
        class: Drupal\my_module\ClassB
        arguments: ['@my_module.obj', '@logger.factory', '@config.factory']
    
    

Simple versions of classes A and B look like this:

ClassA:

<?php

namespace Drupal\my_module;

use GuzzleHttp\ClientInterface;
use Psr\Http\Message\RequestInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;

class A {
  
  /**
   * @var \GuzzleHttp\ClientInterface
   */
  private $httpClient;
  
  public function __construct(ClientInterface $httpClient) {
    $this->httpClient = $httpClient;
  }
}

ClassB:

<?php

namespace Drupal\my_module;

use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\node\NodeInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Psr\Log\LoggerInterface;
use Drupal\Core\Logger\LoggerChannelFactory;

class B implements BInterface {
  
  /**
   * @var \Drupal\my_module\A
   */
  private $a;
  
  /**
   * @var \Drupal\Core\Logger\LoggerChannelFactory
   */
  private $logger;

  public function __construct(A $a, LoggerChannelFactory $logger, ConfigFactoryInterface $config) {
    $this->a = $a;
    $this->logger = $logger;
  }
  
  public static function create(ContainerInterface $container) {
    return new static(
      A::create($container),
      $container->get('my_module.obj'),
      $container->get('logger.factory')->get('my_module'),
      $container->get('config.factory')
    );
  }
}

No matter what I try, I keep running into the same issue of drupal error logs telling me that "argument 1 passed to Drupal\my_module\ClassB::__construct() must be an instance of Drupal\my_module\ClassA, string given"

Edit: Updated classes A and B to reflect changes suggested.

1 Answer 1

3

For this my_module.services.yml file:

services:
  my_module.obj:
    class: Drupal\my_module\ClassA
    arguments: ['@http.client']

You can discard the create() method from your A class, because you declared it as a service for which the arguments get resolved by the services YAML file parameters ie. '@http_client':

class A implements AInterface {
  
  /**
   * @var \GuzzleHttp\ClientInterface
   */
  private $httpClient;
  
  /**
   * @param \GuzzleHttp\ClientInterface $http_client
   *   A Guzzle client object.
   */
  public function __construct(ClientInterface $http_client) {
    $this->httpClient = $http_client;
  }

argument 1 passed to Drupal\my_module\ClassB::__construct() must be an instance of Drupal\my_module\ClassA, string given

Fix this by correcting your B class create() method to pass your service A name being 'my_module.obj', which would then in turn correctly pass an instance to the constructor of class B:

class B implements ContainerInjectionInterface {
  
  /**
   * @var \Drupal\my_module\AInterface
   */
  private $a;

  /**
   * @var \Drupal\Core\Logger\LoggerChannelFactory
   */
  private $logger;

  /**
   * @param \Drupal\my_module\AInterface
   *   Your module A class.
   */
  public function __construct(AInterface $a, LoggerChannelFactory $logger, ConfigFactoryInterface $config) {
    $this->a = $a;
    $this->logger = $logger;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('my_module.obj'),
      $container->get('logger.factory')->get('my_module'),
      $container->get('config.factory')
    );
  }
4
  • I updated my original post to reflect the changes you suggested. Now instead of getting an error that argument 1 to the constructor is a string instead of an instance of ClassA, it tells me that argument 1 is an instance of LoggerChannelFactory when it should be an instance of A. It's like it's ignoring the $container->get('my_module.obj') line in the create() method.
    – Jordan
    Commented Dec 3, 2020 at 21:50
  • Also, which option (1 or 2 from my original post) should I be using for my services.yml file?
    – Jordan
    Commented Dec 3, 2020 at 21:58
  • Please use the 1st option from your post, and yes the arguments need to add up with your contructor. For that remaining argument error please discard A::create($container), from Class B, that is now resolved by $container->get('my_module.obj') instead.
    – baikho
    Commented Dec 3, 2020 at 22:09
  • Ohhh duh I should have realized I needed to remove that A::create($container), part. That fixed it, thank you a ton!
    – Jordan
    Commented Dec 3, 2020 at 22:20

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.