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.

2

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. Dec 3 '20 at 21:50
  • Also, which option (1 or 2 from my original post) should I be using for my services.yml file? Dec 3 '20 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
    Dec 3 '20 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! Dec 3 '20 at 22:20

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.