I’m upgrading from TYPO3 v10 to v11.
In TYPO3 v10 this worked:
<?php
use TYPO3CMSFormDomainModelFormElementsGenericFormElement;
class ServiceSelectFormElement extends GenericFormElement
{
public function __construct(string $identifier, string $type, ApiService $apiClient)
{
$this->apiClient = $apiClient;
parent::__construct($identifier, $type);
}
}
In TYPO3 v11 it does not work anymore, and I get an error:
Uncaught TYPO3 Exception: Too few arguments to function VendorExampleFormElementServiceSelectFormElement::__construct(), 2 passed in /var/www/typo3/public/typo3/sysext/core/Classes/Utility/GeneralUtility.php on line 3215 and exactly 3 expected
So I have to go back from constructor injection to inject*()
method injection:
<?php
use TYPO3CMSFormDomainModelFormElementsGenericFormElement;
class ServiceSelectFormElement extends GenericFormElement
{
public function injectApiClient(ApiService $apiClient): void
{
$this->apiClient = $apiClient;
}
.. but this inject method is not called automatically, unless we mark the class public and enable autowiring in Configuration/Services.yaml
:
services:
VendorExampleFormElementServiceSelectFormElement:
public: true
autowire: true
calls:
- method: injectApiClient
arguments:
$apiClient: '@VendorExampleServiceApiService'
but this leads to another error, because it tries to inject the constructor parameters:
[ SymfonyComponentDependencyInjectionExceptionRuntimeException ] Cannot autowire service "VendorExampleFormElementServiceSelectFormElement": argument "$identifier" of method "TYPO3CMSFormDomainModelFormElementsAbstractFormElement::__construct()" is type-hinted "string", you should configure its value explicitly.
Now my question: How can I prevent TYPO3/Symfony from injecting constructor parameters while still calling my inject*()
method?
2
Answers
Dependency Injection in TYPO3 11, 12 and 13 only works when no constructor parameters are given to
GeneralUtility::makeInstance
- even though method injection could work if it were implemented.The solution is to manually load dependencies in the constructor or when the services are used.
The docs need to be updated.
You need to configure the constructor arguments (
$identifier
,$type
) in yourServices.yaml
to avoid Symfony trying to autowire them. Then, you separately define the call forinjectApiClient
.YAML
autowire
=> false: prevent Symfony from attempting to autowire the constructor arguments automatically.arguments
=> sets default values for$identifier
and$type
in the constructor.calls
=> configureinjectApiClient()
to be called with theApiService
dependency.TYPO3 v11 gives importance to the use of
inject*()
over constructor injection, you should change your class to use setter injection.PHP
This should do it!!!