# Embedding of services with contributte/di

Contributte packages with custom services registration (like contributte/monolog, contributte/mail or contributte/flysystem) in last, Nette 3 compatible version, support flexible registration of services.

You can now either register services same way as in services neon section or reference an existing service. All of the following syntaxes are possible:

post:
  mailer: Contributte\Mail\Mailer\SendmailMailer
  # or
  mailer: Contributte\Mail\Mailer\SendmailMailer()
  # or
  mailer:
    factory: Contributte\Mail\Mailer\SendmailMailer
  # or
  mailer: @mailerServiceName
1
2
3
4
5
6
7
8
9

It is for case when it's useful to be more explicit about which services the extension should use instead of finding all services by an interface.

To implement it in your extension we prepared little helper in contributte/di.

$definitionsHelper = new ExtensionDefinitionsHelper($this->compiler);
$serviceDefinition = $definitionsHelper->getDefinitionFromConfig($serviceConfig, $preferedServiceName);
1
2
  • $preferedServiceName is used for service name only if service already don't have one (case with @mailerServiceName)
  • $serviceDefinition is usually a Definition instance, but it could also be a service name represented by string (e.g. when helper is used in loadConfiguration() phase while service is defined in services section of neon - services from neon are not loaded yet so we can work only with reference)

Service registered in this way should be also validated semantically. Use this little snippet in your config schema Expect::anyOf(Expect::string(), Expect::array(), Expect::type(Statement::class)).

We are using it in contributte/mail just like that.

public function getConfigSchema(): Schema
{
  return Expect::structure([
    'mode' => Expect::anyOf(...self::MODES)->default(self::MODE_STANDALONE),
    'mailer' => Expect::anyOf(Expect::string(), Expect::array(), Expect::type(Statement::class))->required(),
    'debug' => Expect::bool(false),
  ]);
}
1
2
3
4
5
6
7
8