TYPO3 Dependency Injection, next level!

Johannes Przymusinski 28. November 2022 3 Min. Lesezeit

Dependency Injection in TYPO3 ist mächtig, aber wie können wir das noch besser und unabhängiger nutzen?


Unser Ziel ist es, ein Feature als TYPO3 Extension zu entwickeln, welches aber so unabhängig von TYO3 wie möglich sein sollte.
Es soll somit möglich sein das Feature zukünftig auch in “nicht-TYPO3” Umgebungen verwenden zu können ohne alles neu schreiben zu müssen.

Der Plan ist also, das Feature als Composer Package zu entwickeln und eine TYPO3 Extension zu erstellen, die als Art “Adapter” dient um die Funktionen in TYPO3 nutzen zu können.

Aber warum sollte man das machen?

Dafür kann es viele Gründe geben, mein initialer Use-Case war es eine Erweiterung für ein TYPO3 System zu entwickeln, welche in Zukunft einfach in ein eigenständiges Symfony Projekt übertragen werden soll.

Was ist die Idee?

Der Wunsch ist die folgende Ordner-Struktur:

<typo3-root>
├── packages
│   ├── typo3-extension
│   │   ├── Classes
│   │   ├── Configuration
│   │   │   └── Services.php           <- DI Konfiguration für "typo3-extension"
│   │   └── ext_emconf.php
│   └── feature
│       ├── src
│       │   ├── <Services and Classes>  <- Klassen des "Core" Features
│       │   └── Resources
│       │       └── config
│       │           └── service.xml     <- DI Konfiguration für "feature"
│       └── tests
├── public
├── copmoser.json
├── ...
---
title: "TYPO3 Dependency Injection, next level!"
author: "Johannes Przymusinski"
shorttext: "Dependency Injection in TYPO3 ist mächtig — wir zeigen, wie man es besser und unabhängiger nutzen kann."
publishedAt: 2022-11-28
readingTime: 5
---

Seit Version 10 hat TYPO3 Dependency Injection auf Basis von `symfony/dependency-injection` an Board.

Der Plan ist nun, die `src/Resources/congig/services.xml` Datei aus dem Bundle zu laden, sodass wir sowohl wir
dependency injection sowohl innerhalb des Bundles als auch der TYPO3 Implementierung verwenden können.

Symfony Bundles machen dies mit einer sog. `Extension` für den Container Builder also schreiben wir auch eine.

Extensions liegen in Symfony Bundles in `src/DependencyInjection`, daher lege ich dieser auch dort an.

```php
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Extension\Extension;

class FeatureExtension extends Extension implements CompilerPassInterface
{
    public function load(array $configs, ContainerBuilder $container): void
    {
        $this->process($container);
    }

    public function process(ContainerBuilder $container)
    {
        $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . "/../Resources/config"));

        $loader->load("services.xml");
    }
}

Wichtig ist hierbei, dass wir sowohl die Klasse Extension erweitern als auch das CompilerPassInterface implementieren.

Die Basis-Klasse Extension wird benötigt, sodass wir die Extension im Container Builder registrieren können. Die eigentliche Ausführung der Extension passiert dann aber als CompilerPass, hierfür wird das CompilerPassInterface benötigt.

Das alleine sorgt aber leider nicht magisch dafür, dass die Services im DI-Container verfügbar sind.
Wir müssen TYPO3 noch sagen, dass die Extension geladen werden soll, dies geht am einfachsten mit der Configuration/Services.php Datei.

use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator;

return static function (ContainerConfigurator $containerConfigurator, ContainerBuilder $containerBuilder): void {
    $containerBuilder->registerExtension(new FeatureExtension());
};

Verwenden der registrieren Services

Nun, da wir die Services in der Dependency-Injection registriert haben, können wir diese tatsächlich genau so verwenden wie Services, die direkt aus einer TYPO3-Extension kommen.

Mehr darüber erfährst du in der TYPO3 Dokumentation zur Dependency-Injection