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(NetteMode) # 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('NetteMode')
);
}
Special Functions
You can also use special functions in arguments to cast or negate values:
not(%arg%)
negationbool(%arg%)
lossless cast to boolint(%arg%)
lossless cast to intfloat(%arg%)
lossless cast to floatstring(%arg%)
lossless cast to string
services:
- Foo(
id: int(::getenv('ProjectId'))
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