Automatic factories
A separate factory class for every controller and service is a lot of repetitive code. The laminas-servicemanager package has two ready-made factories that fill in constructor dependencies themselves. Nothing has to be installed — the package is already in the project.
ReflectionBasedAbstractFactory
This factory reads the types of the constructor arguments and takes services with the same names from the container. Delete the PostControllerFactory.php file from the previous lesson and change the configuration:
// module/Blog/config/module.config.php
// at the top of the file:
// use Laminas\ServiceManager\AbstractFactory\ReflectionBasedAbstractFactory;
'controllers' => [
'factories' => [
Controller\PostController::class => ReflectionBasedAbstractFactory::class,
],
],The controller's constructor stays the same — __construct(private ReadingTime $readingTime). The factory sees the ReadingTime type, finds the Blog\Service\ReadingTime service in the container and passes it in. The /blog/1 page works as before.
If you add another service to the constructor, the configuration does not need to change.
What it can fill in
| Constructor argument | What is passed |
|---|---|
| Typed with a class or interface | The service with that name from the container |
array $config | The whole application configuration array |
| An argument with a default value | The default value |
| A string or number without a default | An error: the factory does not know what to pass |
ConfigAbstractFactory
The second factory does not inspect the class; it reads the list of dependencies from the configuration. The list is written under the ConfigAbstractFactory::class key:
// module/Blog/config/module.config.php
// at the top of the file:
// use Laminas\ServiceManager\AbstractFactory\ConfigAbstractFactory;
'controllers' => [
'factories' => [
Controller\PostController::class => ConfigAbstractFactory::class,
],
],
ConfigAbstractFactory::class => [
Controller\PostController::class => [
Service\ReadingTime::class,
],
],The array lists the services in the order the constructor expects them. The result is the same, but the dependencies are visible right in the config file.
Which one to choose
| Approach | Pros | Cons |
|---|---|---|
| Your own factory class | Any logic, the fastest | A lot of code |
ReflectionBasedAbstractFactory | Nothing to write | Slightly slower: the class is inspected on every creation |
ConfigAbstractFactory | Dependencies are visible in the config | The list must be updated by hand |
Throughout the course, controllers and simple services are registered with ReflectionBasedAbstractFactory. You write your own factory when an object needs something other than services: a config value, a table name, a file path.
Generating factories
For production, ordinary factories can be generated instead of written by hand. The package ships a console program:
vendor/bin/generate-factory-for-class "Blog\Controller\PostController" > module/Blog/src/Controller/PostControllerFactory.phpThe command prints the factory code, and the > sign saves it to a file. All that is left is to name that class in the controllers section.
Common mistake
ReflectionBasedAbstractFactory looks a service up by the type name. If the Blog\Service\ReadingTime class is not registered in service_manager, you get a ServiceNotFoundException naming that class — even though the error happens while the controller is being created. Read the service name in the message and register exactly that one.