Initial version for webcamp venlo

This commit is contained in:
Daniel Siepmann 2023-03-06 15:56:31 +01:00
parent af48695455
commit bc9a00601e
Signed by: Daniel Siepmann
GPG key ID: 33D6629915560EF4
3 changed files with 309 additions and 3 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/Readme.pdf

View file

@ -1,3 +0,0 @@
# talk-typo3-dependency-injection
An practical introduction into Dependency Injection with Symfony Dependency Injection for TYPO3.

308
Readme.rst Normal file
View file

@ -0,0 +1,308 @@
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_<hash>.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:
<?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
^^^^^^^^^^^^^^^^^^^^^^^
.. 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:
<?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
^^^^^^^
.. 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:
<?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)
->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:
<?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/