Problem/Motivation

A module that has autowired services that depend on a to-be-installed module cannot be deployed automatically.

module1 has a my.service:

services:
  my.service:
    class: Drupal\mymodule\Service\MyService
    autowire: true

The service depends on My2Service

class MyService {

  public function __construct(protected My2Service $my2Service) {}
}

module2 has a my2.service

services:
  my2.service:
    class: Drupal\my2module\Service\My2Service
    autowire: true
  Drupal\my2module\Service\My2Service: '@my2.service'

Steps to reproduce

1. Enable module 1 without autowiring changes
2. Make autowiring changes that depend on new module 2
3. Add update hook to enable module 2
4. Run `drush updb`

Error:

In DefinitionErrorExceptionPass.php line 51:                                     
                                                                                   
Cannot autowire service "my.service": argument "$my2Service" of method "Drupal\mymodule\Service\MyService::__construct()" has type "\Drupal\my2module\Service\My2Service" but this class couldn't be loaded. Either it was not found or it is missing a parent class or a trait.  

This is manually fixable by using drush to enable the missing module. Using an update hook to enable the module does not work, as it errors out before running the hook.

Proposed resolution

Remaining tasks

User interface changes

Introduced terminology

API changes

Data model changes

Release notes snippet

Comments

djdevin created an issue. See original summary.

cilefen’s picture

Component: bootstrap system » base system

Module classes are not in the container or autoloaded if the module is not installed so I think this is to be expected? Does module2 depend on module1 in its info.yml file?

djdevin’s picture

Yes, the dependencies are setup correctly (mod1 depends on mod2).

On a fresh install it works fine, issue seems to be introducing autowiring during an update - seems to be no way I can get the module enabled in time before that error throws.

cilefen’s picture

You may have to write a service factory class in module1 to dynamically load the new dependency if available or else do something else. I don't know whether this can be "fixed" as if it is a bug.

quietone’s picture

Version: 10.3.x-dev » 11.x-dev

Changes are made on on 11.x (our main development branch) first, and are then back ported as needed according to the Core change policies.

catch’s picture

There's some amount of support for optional dependencies in the container, see https://symfony.com/doc/current/service_container/optional_dependencies....

I'm not sure exactly if/how this is supported with autowiring, but you might be able to do that, then make the service non-optional once you can ensure that every site has updated to the version with the new dependency.

nicxvan’s picture

Does this belong in the recurrent component?

mstrelan’s picture

A similar issue occurs when autowiring a service that implements an interface from a module that is not installed. This appears to be a valid thing to do when decorating a service, by using decoration_on_invalid: ignore for when the module is not installed. This works completely fine when manually wiring up the service. It fails during \Symfony\Component\DependencyInjection\Compiler\AutowireAsDecoratorPass::process as it tries to load the class.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.