Service Definitions

Configuration is where we place the definitions of custom services. This is done in section services.

For example, this is how we create a service named database, which will be an instance of class PDO:

services:
	database: PDO('sqlite::memory:')

The naming of services is used to allow us to reference them. If a service is not referenced, there is no need to name it. So we just use a bullet point instead of a name:

services:
	- PDO('sqlite::memory:')  #  anonymous service

A one-line entry can be broken up into multiple lines to allow additional keys to be added, such as setup. The alias for the create: key is factory:.

services:
	database:
		create: PDO('sqlite::memory:')
		setup: ...

We then retrieve the service from the DI container using the method getService() by name, or better yet, the method getByType() by type:

$database = $container->getService('database');
$database = $container->getByType(PDO::class);

Creating a Service

Most often, we create a service by simply creating an instance of a class:

services:
	database: PDO('mysql:host=127.0.0.1;dbname=test', root, secret)

Which will generate a factory method in DI container:

public function createServiceDatabase(): PDO
{
	return new PDO('mysql:host=127.0.0.1;dbname=test', 'root', 'secret');
}

Alternatively, a key arguments can be used to pass arguments:

services:
	database:
		create: PDO
		arguments: ['mysql:host=127.0.0.1;dbname=test', root, secret]

A static method can also create a service:

services:
	database: My\Database::create(root, secret)

Corresponds to PHP code:

public function createServiceDatabase(): PDO
{
	return My\Database::create('root', 'secret');
}

A static method My\Database::create() is assumed to have a defined return value that the DI container needs to know. If it does not have it, we write the type to the configuration:

services:
	database:
		create: My\Database::create(root, secret)
		type: PDO

Nette DI gives you extremely powerful expression facilities to write almost anything. For example, to refer to another service and call its method. For simplicity, :: is used instead of ->.

services:
	routerFactory: App\Router\Factory
	router: @routerFactory::create()

Corresponds to PHP code:

public function createServiceRouterFactory(): App\Router\Factory
{
	return new App\Router\Factory;
}

public function createServiceRouter(): Router
{
	return $this->getService('routerFactory')->create();
}

Method calls can be chained together as in PHP:

services:
	foo: FooFactory::build()::get()

Corresponds to PHP code:

public function createServiceFoo()
{
	return FooFactory::build()->get();
}

Arguments

Named parameters can also be used to pass arguments:

services:
	database: PDO(
		'mysql:host=127.0.0.1;dbname=test'  # positional
		username: root                      # named
		password: secret                    # named
	)

The use of commas is optional when breaking arguments into multiple lines.

Of course, we can also use other services or parameters as arguments:

services:
	- Foo(@anotherService, %appDir%)

Corresponds to PHP code:

public function createService01(): Foo
{
	return new Foo($this->getService('anotherService'), '...');
}

If the first argument is autowired and you want to specify the second one, omit the first one with the _ character, for example Foo(_, %appDir%). Or better yet, pass only the second argument as a named parameter, e.g. Foo(path: %appDir%).

Nette DI and the NEON format give you extremely powerful expressive facilities to write almost anything. Thus an argument can be a newly created object, you can call static methods, methods of other services, or even global functions using special notation:

services:
	analyser: My\Analyser(
		FilesystemIterator(%appDir%)         # create object
		DateTime::createFromFormat('Y-m-d')  # call static method
		@anotherService                      # passing another service
		@http.request::getRemoteAddress()    # calling another service method
		::getenv(NETTE_MODE)                 # call a global function
	)

Corresponds to PHP code:

public function createServiceAnalyser(): My\Analyser
{
	return new My\Analyser(
		new FilesystemIterator('...'),
		DateTime::createFromFormat('Y-m-d'),
		$this->getService('anotherService'),
		$this->getService('http.request')->getRemoteAddress(),
		getenv('NETTE_MODE')
	);
}

Special Functions

You can also use special functions in arguments to cast or negate values:

  • not(%arg%) negation
  • bool(%arg%) lossless cast to bool
  • int(%arg%) lossless cast to int
  • float(%arg%) lossless cast to float
  • string(%arg%) lossless cast to string
services:
	- Foo(
		id: int(::getenv('PROJECT_ID'))
		productionMode: not(%debugMode%)
	)

Lossless rewriting differs from normal PHP rewriting, e.g. using (int), in that it throws an exception for non-numeric values.

Multiple services can be passed as arguments. An array of all services of a particular type (i.e., class or interface) is created by function typed(). The function will omit services that have autowiring disabled, and multiple types separated by a comma can be specified.

services:
	- BarsDependent( typed(Bar) )

You can also pass an array of services automatically using autowiring.

An array of all services with a certain tag is created by function tagged(). Multiple tags separated by a comma can be specified.

services:
	- LoggersDependent( tagged(logger) )

Referencing Services

Individual services are referenced using character @ and name, so for example @database:

services:
	- create: Foo(@database)
	  setup:
			- setCacheStorage(@cache.storage)

Corresponds to PHP code:

public function createService01(): Foo
{
	$service = new Foo($this->getService('database'));
	$service->setCacheStorage($this->getService('cache.storage'));
	return $service;
}

Even anonymous services can be referenced using a callback, just specify their type (class or interface) instead of their name. However, this is usually not necessary due to autowiring.

services:
	- create: Foo(@Nette\Database\Connection)  # or @\PDO
	  setup:
			- setCacheStorage(@cache.storage)

Setup

In the setup section we list the methods to be called when creating the service:

services:
	database:
		create: PDO(%dsn%, %user%, %password%)
		setup:
			- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)

Corresponds to PHP code:

public function createServiceDatabase(): PDO
{
	$service = new PDO('...', '...', '...');
	$service->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
	return $service;
}

Properites can also be set. Adding an element to an array is also supported, and should be written in quotes so as not to conflict with NEON syntax:

services:
	foo:
		create: Foo
		setup:
			- $value = 123
			- '$onClick[]' = [@bar, clickHandler]

Corresponds to PHP code:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	$service->value = 123;
	$service->onClick[] = [$this->getService('bar'), 'clickHandler'];
	return $service;
}

However, static methods or methods of other services can also be called in the setup. We pass the actual service to them as @self:

services:
	foo:
		create: Foo
		setup:
			- My\Helpers::initializeFoo(@self)
			- @anotherService::setFoo(@self)

Corresponds to PHP code:

public function createServiceFoo(): Foo
{
	$service = new Foo;
	My\Helpers::initializeFoo($service);
	$this->getService('anotherService')->setFoo($service);
	return $service;
}

Autowiring

The autowired key can be used to exclude a service from autowiring or to influence its behavior. See chapter on autowiring for more information.

services:
	foo:
		create: Foo
		autowired: false     # foo is removed from autowiring

Tags

User information can be added to individual services in the form of tags:

services:
	foo:
		create: Foo
		tags:
			- cached

Tags can also have a value:

services:
	foo:
		create: Foo
		tags:
			logger: monolog.logger.event

An array of services with certain tags can be passed as an argument using the function tagged(). Multiple tags separated by a comma can also be specified.

services:
	- LoggersDependent( tagged(logger) )

Service names can be obtained from the DI container using the method findByTag():

$names = $container->findByTag('logger');
// $names is an array containing the service name and tag value
// i.e. ['foo' => 'monolog.logger.event', ...]

Inject Mode

The inject: true flag is used to activate the passing of dependencies via public variables with the inject annotation and the inject*() methods.

services:
	articles:
		create: App\Model\Articles
		inject: true

By default, inject is only activated for presenters.

Modification of Services

There are a number of services in the DI container that have been added by built-in or your extension. The definitions of these services can be modified in the configuration. For example, for service application.application, which is by default an object Nette\Application\Application, we can change the class:

services:
	application.application:
		create: MyApplication
		alteration: true

The alteration flag is informative and says that we are just modifying an existing service.

We can also add a setup:

services:
	application.application:
		create: MyApplication
		alteration: true
		setup:
			- '$onStartup[]' = [@resource, init]

When rewriting a service, we may want to remove the original arguments, setup items or tags, which is what reset is for:

services:
	application.application:
		create: MyApplication
		alteration: true
		reset:
			- arguments
			- setup
			- tags

A service added by extension can also be removed from the container:

services:
	cache.journal: false