An practical introduction into Dependency Injection with Symfony Dependency Injection for TYPO3.
.gitignore | ||
LICENSE | ||
Readme.rst |
Talk TYPO3 Dependency Injection
A practical introduction into Dependency Injection with Symfony Dependency Injection for TYPO3.
What is Dependency Injection
- Our code has dependencies (E.g. Controller → View, Controller → Repository)
- Inversion of control (ask others to inject dependencies)
- Allow configuration of dependencies
- Do yourself a favour and use interfaces
- Used to connect pieces of code (E.g. event listener, commands, …)
Flow
TYPO3 will:
- Check all
Services.yaml
andServices.php
files once - Use Symfony Dependency Injection once
- To build a psr/container (PSR-11) once
- Then load the psr/container on consecutive calls
- Use psr/container to fetch concrete implementations
The generated code (Container) is stored at:
var/cache/code/di/DependencyInjectionContainer_<hash>.php
Usage within TYPO3
Injection
A consumer needs dependencies.
- Injection via
__construct()
- Injection via
inject*()
method - Injection via
@required
annotated method
Manually create instances
Always use GeneralUtility::makeInstance()
.
- 0 arguments will ask the container
- Requested service needs to be public
Public services
Prevent them!
Mark as public when TYPO3 forces you to do so.
The Services.yaml
Basic example if you prefer YAML:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
DanielSiepmann\Tracking\:
resource: '../Classes/*'
The Services.php
Basic example if you prefer PHP:
<?php
declare(strict_types=1);
namespace WerkaumMedia\ABTest\Configuration;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
return static function (
ContainerConfigurator $containerConfigurator
) {$services = $containerConfigurator
->services()
->defaults()
->autowire()
->autoconfigure();
$services->load('WerkraumMedia\\ABTest\\', '../Classes/');
; }
Concrete Examples
Injecting Query Builder
services:
_defaults:
autowire: true
autoconfigure: true
public: false
DanielSiepmann\Tracking\:
resource: '../Classes/*'
querybuilder.tx_tracking_pageview:
class: 'TYPO3\CMS\Core\Database\Query\QueryBuilder'
factory:
- '@TYPO3\CMS\Core\Database\ConnectionPool'
- 'getQueryBuilderForTable'
arguments:
- 'tx_tracking_pageview'
DanielSiepmann\Tracking\Domain\Repository\Pageview:
public: true
arguments:
- '@querybuilder.tx_tracking_pageview'
See: https://git.daniel-siepmann.de/danielsiepmann/tracking/src/branch/main/Configuration/Services.yaml
Event Listener
<?php
declare(strict_types=1);
namespace WerkraumMedia\ABTest\Configuration;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use WerkraumMedia\ABTest\Events\SwitchedToVariant;
use WerkraumMedia\ABTest\MatomoTracker;
return static function (ContainerConfigurator $containerConfigurator) {
$services = $containerConfigurator
->services()
->defaults()
->autowire()
->autoconfigure();
$services->load('WerkraumMedia\\ABTest\\', '../Classes/');
$services->set(MatomoTracker::class)->tag('event.listener', [
'method' => 'handleVariant',
'event' => SwitchedToVariant::class,
;
]); }
See: https://git.daniel-siepmann.de/Customers/abtest/src/branch/main/Configuration/Services.php
Command
:
services_defaults:
: true
autowire: true
autoconfigurepublic: false
DanielSiepmann\Tracking\:
resource: '../Classes/*'
DanielSiepmann\Tracking\Command\UpdateDataCommand:
:
tags- name: 'console.command'
: 'tracking:updatedata' command
See: https://git.daniel-siepmann.de/danielsiepmann/tracking/src/branch/main/Configuration/Services.yaml
CompilerPass
- Alter existing service definitions
- E.g. add tagged classes to a registry
- E.g. add event listeners
- E.g. mark classes as public
<?php
declare(strict_types=1);
namespace WerkraumMedia\ThueCat;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;
use WerkraumMedia\ThueCat\Domain\Import\Entity\MapsToType;
use WerkraumMedia\ThueCat\Domain\Import\Typo3Converter\Converter;
use WerkraumMedia\ThueCat\Domain\Import\UrlProvider\UrlProvider;
return function (ContainerConfigurator $container, ContainerBuilder $containerBuilder) {
$containerBuilder->registerForAutoconfiguration(UrlProvider::class)
DependencyInjection\UrlProvidersPass::TAG);
->addTag($containerBuilder->addCompilerPass(new DependencyInjection\UrlProvidersPass());
$containerBuilder->registerForAutoconfiguration(Converter::class)
DependencyInjection\ConverterPass::TAG);
->addTag($containerBuilder->addCompilerPass(new DependencyInjection\ConverterPass());
$containerBuilder->registerForAutoconfiguration(MapsToType::class)
DependencyInjection\EntityPass::TAG);
->addTag($containerBuilder->addCompilerPass(new DependencyInjection\EntityPass());
;
}
.. code-block:: php
:linenos:
<?php
declare(strict_types=1);
namespace WerkraumMedia\ThueCat\DependencyInjection;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use WerkraumMedia\ThueCat\Domain\Import\Typo3Converter\Registry;
class ConverterPass implements CompilerPassInterface
{public const TAG = 'thuecat.typo3.converter';
public function process(ContainerBuilder $container): void
{$registry = $container->findDefinition(Registry::class);
foreach ($container->findTaggedServiceIds(self::TAG) as $id => $tags) {
$definition = $container->findDefinition($id);
if (!$definition->isAutoconfigured() || $definition->isAbstract()) {
continue;
}
$registry->addMethodCall('registerConverter', [$definition]);
}
} }
See:
- https://git.daniel-siepmann.de/Customers/thuecat/src/branch/main/Configuration/Services.php
- https://git.daniel-siepmann.de/Customers/thuecat/src/branch/main/Classes/DependencyInjection
- https://symfony.com/doc/current/service_container/compiler_passes.html
Sources
- https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DependencyInjection/Index.html
- https://symfony.com/doc/current/components/dependency_injection.html
- https://symfony.com/doc/current/service_container.html
- https://usetypo3.com/dependency-injection.html
- https://usetypo3.com/di-and-events-example.html
- https://daniel-siepmann.de/concrete-typo3-dependency-injection-examples.html
- https://daniel-siepmann.de/prepare-legacy-code-for-upcoming-typo3-versions.html
- https://en.wikipedia.org/wiki/Dependency_injection
- https://www.php-fig.org/psr/psr-11/