Invoke before configuring Nette DI - services, .neon files, autowiring.
services.neon โ all service definitionscommon.neon โ parameters, extensions, framework settings, includesapi.neon, tasks.neon)env.local.neon for development, env.prod.neon for productioncommon.neon includes other files. Later files override earlier ones, so environment-specific files go last:
includes:
- services.neon
- env.local.neon # Gitignored, overrides for local dev
Register DI extensions for third-party packages or custom container builders:
extensions:
console: Contributte\Console\DI\ConsoleExtension
translation: Contributte\Translation\DI\TranslationExtension
Use - for services that don't need references. Nette DI automatically resolves all constructor dependencies by type, so most services need just their class name:
services:
- App\Model\BlogFacade
- App\Model\CustomerService
- App\Presentation\Accessory\TemplateFilters
Give names only when needed for @serviceName references elsewhere in NEON. Unnecessary names add clutter and create a maintenance burden when classes are renamed:
services:
# Named because referenced as @pohoda
pohoda:
create: Nette\Database\Connection('odbc:Driver={...}')
autowired: false
# Using the reference
- App\Model\PohodaImporter(pohoda: @pohoda)
When manually specifying arguments, pass them by name; only a single argument that is the constructor's first may stay positional. Positional arguments break when the constructor signature changes:
services:
# Good - first parameter, clear order
- App\Model\ImageService(%rootDir%/storage)
# Good - named parameter when mixing with autowiring
- App\Model\CustomerService(ip: %ip%)
- App\Model\BlogFacade(blogPath: %blog%)
services:
- App\Core\RouterFactory::createRouter
- Symfony\Component\HttpClient\HttpClient::create()
services:
- App\Model\MailImporter(
DG\Imap\Mailbox(
mailbox: %imap.mailbox%
username: %imap.username%
password: %imap.password%
)
debugMode: %debugMode%
)
services:
database:
create: PDO(%dsn%, %user%, %password%)
setup:
- setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION)
When you need to alter an existing service (e.g. replace a framework service with your own implementation), you can refer to it by type using @ClassName: instead of guessing internal service names. The alteration: true is implicit:
services:
# Override the framework's Application with a custom one
@Nette\Application\Application:
create: MyApplication
Use autowired: false when you have multiple services of the same type and need to prevent ambiguity (e.g. two database connections):
services:
specialDb:
create: Nette\Database\Connection(...)
autowired: false # Must reference explicitly via @specialDb
Use search section to avoid manually registering services that follow naming conventions. This keeps services.neon short and ensures new services are picked up automatically:
search:
model:
in: %appDir%
classes: # Classes ending with Factory, Facade, or Service
- *Factory
- *Facade
- *Service
Instead of injecting the container or writing factory boilerplate, declare an interface and let
DI generate the implementation. The interface must have exactly one method named create
with a declared return type:
interface ArticleFactory
{
function create(int $authorId): Article;
}
services:
- ArticleFactory # short form: implement: is inferred from the interface
Parameters of create() are matched to the constructor by name, so $authorId above lands
in Article::__construct(int $authorId). The long form adds fixed arguments or setup:
services:
articleFactory:
implement: ArticleFactory
arguments:
authorId: 123 # create() does not take it, the constructor does
setup:
- setAuthorId($authorId) # create() takes it, a setter consumes it
An accessor is the lazy variant โ one get() method returning an existing service, so the
service is only created on first get():
interface DatabaseAccessor
{
function get(): Database;
}
This is the mechanism behind component factories in presenters, so use it instead of passing the container around.
Apply configuration to all services of a specific type without listing them individually. Useful for injecting common dependencies or calling setup methods on multiple services:
decorator:
App\Presentation\BasePresenter:
setup:
- setTranslator
Reference configuration parameters with %parameterName%. Parameters are defined in the parameters section of common.neon and can be overridden per environment:
services:
- App\Model\TexyProcessor('', %wwwDir%, %tempDir%/texy)
- App\Tasks\ImportTask(path: %rootDir%/../data/import)
- App\Model\CustomerService(ip: %ip%)
Via service arguments (recommended) โ explicit, type-safe, visible in constructor:
# config/services.neon
services:
- App\Presentation\Admin\Product\ProductPresenter(itemsPerPage: 20)
class ProductPresenter extends BasePresenter
{
public function __construct(
private int $itemsPerPage,
private ProductFacade $facade,
) {}
}
Via parameters โ useful when the same value is used in multiple places:
# config/common.neon
parameters:
pagination:
itemsPerPage: 20
maxItems: 1000
# config/services.neon
services:
- App\Presentation\Admin\Product\ProductPresenter(
itemsPerPage: %pagination.itemsPerPage%
)
Add sections to common.neon only when you need specific functionality.
Add when: Setting up error handling or custom URL mapping
application:
mapping: App\Presentation\*\**Presenter
errorPresenter:
4xx: Error:Error4xx # When you have custom error pages
5xx: Error:Error5xx
aliases: # When you have frequently used links in n:href or $presenter->link()
home: 'Front:Home:'
admin: 'Admin:Dashboard:'
Add when: You need custom cookie settings, proxy support, or session configuration
http:
proxy: 10.0.0.0/8 # When behind a reverse proxy
session:
expiration: 14 days
cookieSamesite: Lax
Add when: You need simple file-based authentication for development or prototyping. For production, implement a custom authenticator class instead.
security:
users: # Development only - NOT for production
username: password
Only classes that need injection or are injected elsewhere. Presenters are registered automatically (Nette scans them by IPresenter); component factory interfaces are not: list them in services:, or let a search: section find them (it picks up interfaces with a single create() or get() method).
Credentials do not belong in committed config files, but where they live is the project's decision. Follow the convention the project already has; when it has none, offer the user the options instead of picking one:
secrets.neon, or one file per environment such as env.local.neon), included from common.neon and referenced as %dbPassword%Bootstrap by $this->configurator->addDynamicParameters(['env' => getenv()]) and referenced as %env.DB_PASSWORD%; as dynamic parameters they are read at runtime, not frozen into the compiled containerFor details, see the official documentation: