Talk TYPO3 Dependency Injection =============================== A practical introduction into Dependency Injection with Symfony Dependency Injection for TYPO3. .. contents:: :local: 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: 1. Check all ``Services.yaml`` and ``Services.php`` files once 2. Use Symfony Dependency Injection once 3. To build a psr/container (PSR-11) once 4. Then load the psr/container on consecutive calls 5. Use psr/container to fetch concrete implementations The generated code (Container) is stored at: ``var/cache/code/di/DependencyInjectionContainer_.php`` Usage within TYPO3 ------------------ Injection ^^^^^^^^^ A consumer needs dependencies. - Injection via ``__construct()`` - Injection via ``inject*()`` method - Injection via ``@required`` annotated method See: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DependencyInjection/Index.html#constructor-injection 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. See: https://docs.typo3.org/m/typo3/reference-coreapi/main/en-us/ApiOverview/DependencyInjection/Index.html#what-to-make-public The Services.yaml ----------------- Basic example if you prefer YAML: .. code-block:: yaml :linenos: services: _defaults: autowire: true autoconfigure: true public: false DanielSiepmann\Tracking\: resource: '../Classes/*' The Services.php ----------------- Basic example if you prefer PHP: .. code-block:: php :linenos: services() ->defaults() ->autowire() ->autoconfigure() ; $services->load('WerkraumMedia\\ABTest\\', '../Classes/'); }; Concrete Examples ----------------- Injecting Query Builder ^^^^^^^^^^^^^^^^^^^^^^^ .. code-block:: yaml :linenos: 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 ^^^^^^^^^^^^^^ .. code-block:: php :linenos: 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 ^^^^^^^ .. code-block:: php :linenos: services: _defaults: autowire: true autoconfigure: true public: false DanielSiepmann\Tracking\: resource: '../Classes/*' DanielSiepmann\Tracking\Command\UpdateDataCommand: tags: - name: 'console.command' command: 'tracking:updatedata' 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 .. code-block:: php :linenos: registerForAutoconfiguration(UrlProvider::class) ->addTag(DependencyInjection\UrlProvidersPass::TAG); $containerBuilder->addCompilerPass(new DependencyInjection\UrlProvidersPass()); $containerBuilder->registerForAutoconfiguration(Converter::class) ->addTag(DependencyInjection\ConverterPass::TAG); $containerBuilder->addCompilerPass(new DependencyInjection\ConverterPass()); $containerBuilder->registerForAutoconfiguration(MapsToType::class) ->addTag(DependencyInjection\EntityPass::TAG); $containerBuilder->addCompilerPass(new DependencyInjection\EntityPass()); }; .. code-block:: php :linenos: 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/