Service Definitions
Configuration is the place where we instruct the DI container on how to assemble individual services and how to connect them with other dependencies. Nette provides a very clear and elegant way to achieve this.
The services
section in the NEON configuration file is where we define our custom services and their
configurations. Let's look at a simple example of defining a service named database
, which represents an instance of
the PDO
class:
This configuration results in the following factory method in the DI container:
Service names allow us to reference them in other parts of the configuration file, using the format @serviceName
.
If there's no need to name the service, we can simply use a bullet point:
To retrieve a service from the DI container, we can use the getService()
method with the service name as a
parameter, or the getByType()
method with the service type:
Service Creation
Most commonly, we create a service simply by instantiating a specific class. For example:
If we need to expand the configuration with additional keys, the definition can be expanded into multiple lines:
The create
key has an alias factory
, both versions are common in practice. However, we recommend
using create
.
Constructor arguments or the creation method can alternatively be written in the arguments
key:
Services don't have to be created just by simple instantiation of a class; they can also result from calling static methods or methods of other services:
Note that for simplicity, instead of ->
, we use ::
, see expression
means. These factory methods are generated:
The DI container needs to know the type of the created service. If we create a service using a method that doesn't have a specified return type, we must explicitly mention this type in the configuration:
Arguments
We pass arguments to constructors and methods in a manner very similar to regular PHP:
For better readability, we can list the arguments on separate lines. In this format, the use of commas is optional:
You can also name the arguments, which then allows you to not worry about their order:
If you wish to omit certain arguments and use their default values or insert a service via autowiring, use an underscore:
Arguments can be services, parameters, and much more, see expression means.
Setup
In the setup
section, we define the methods that should be called when creating the service.
In PHP, this would look like:
In addition to method calls, you can also pass values to properties. Adding an element to an array is also supported, but you need to enclose it in quotes to avoid colliding with the NEON syntax:
In PHP, this would translate to:
In the setup, you can also call static methods or methods of other services. If you need to pass the current service as an
argument, use @self
:
Note that for simplicity, instead of ->
, we use ::
, see expression
means. This generates the following factory method:
Expression Means
Nette DI provides us with exceptionally rich expression capabilities, allowing us to articulate almost anything. In configuration files, we can use parameters:
We can also create objects, call methods, and functions:
Refer to services either by their name or by type:
Use first-class callable syntax:
Use constants:
Method calls can be chained, just like in PHP. For simplicity, instead of ->
, we use ::
:
These expressions can be used anywhere when creating services, in arguments, in the setup section, or parameters:
Special Functions
Within configuration files, you can utilize these special functions:
not()
for value negationbool()
,int()
,float()
,string()
for lossless type castingtyped()
to generate an array of all services of a specified typetagged()
to create an array of all services with a given tag
Compared to conventional typecasting in PHP, like (int)
, lossless type casting will throw an exception for
non-numeric values.
The typed()
function creates an array of all services of a particular type (class or interface). It excludes
services with autowiring turned off. Multiple types can be specified, separated by commas.
You can also automatically pass an array of services of a specific type as an argument using autowiring.
The tagged()
function creates an array of all services with a specified tag. Multiple tags can be listed,
separated by commas.
Autowiring
The autowired
key allows you to modify the autowiring behavior for a particular service. For more details, see the autowiring chapter.
Lazy Services
Lazy loading is a technique that delays the creation of a service until it is actually needed. You can enable lazy service creation globally in the configuration for all services at once. For individual services, this behavior can be overridden:
When a service is defined as lazy, requesting it from the DI container will return a special proxy object. This proxy looks and behaves like the actual service, but the real initialization (constructor call and setup) will only occur upon the first invocation of any of its methods or properties.
Lazy loading can only be used for user-defined classes, not for internal PHP classes. It requires PHP 8.4 or newer.
Tags
Tags are used to add supplementary information to services. You can assign one or more tags to a service:
Tags can also carry values:
To retrieve all services with specific tags, you can use the tagged()
function:
In the DI container, you can obtain the names of all services with a specific tag using the findByTag()
method:
Inject Mode
Using the flag inject: true
activates the passing of dependencies via public variables with the inject annotation and inject*() methods.
By default, inject
is only activated for presenters.
Service Modifications
The DI container contains many services added either by built-in or user extensions. You can
modify the definitions of these services directly in the configuration. For instance, you can change the class of the
application.application
service, which is conventionally Nette\Application\Application
, to
something else:
The alteration
flag is informative, indicating that we're merely modifying an existing service.
We can also supplement the setup:
When overwriting a service, you might want to remove original arguments, setup items, or tags, which is where
reset
comes in handy:
If you wish to remove a service added by an extension, you can do so like this: