Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 argumentWhat is passed
Typed with a class or interfaceThe service with that name from the container
array $configThe whole application configuration array
An argument with a default valueThe default value
A string or number without a defaultAn 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

ApproachProsCons
Your own factory classAny logic, the fastestA lot of code
ReflectionBasedAbstractFactoryNothing to writeSlightly slower: the class is inspected on every creation
ConfigAbstractFactoryDependencies are visible in the configThe 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.php

The 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.

Thanks for reading!

Donate to the site — we work hard for you.