[7963 Aufrufe]

Aufbau einer Contao Erweiterung

In diesem Kapitel zeige ich, wie ich meine Erweiterungen aufbaue. Dies ist das Ergebnis der letzten Jahre und stellt eine Art best practice dar, mit der ich am wenigsten Probleme habe. Es gibt gerade unter Contao 4 und Symfony viele mögliche Wege. Einige Entwickler haben z. B. einen src-Ordner in ihrer Erweiterung, auf den ich verzichte, da er aus meiner Sicht keinen Vorteil bringt.

Es ist aber ganz klar, das dies hier nicht der Königsweg, oder das einzig richtige Vorgehen ist. Es ist nur ein Beispiel für einen funktionierenden Weg.

Namen

In Contao heißen die zusätzlichen Programmpakete in der Regel Erweiterung oder Extension, in Symfony werden sie oft als Bundles bezeichnet. Da in Contao 4 eine Erweiterung ein Bundle sein kann, werde ich die Bezeichnungen gleichwertig benutzen. Mir ist bewusst, dass dies historisch und technisch nicht ganz korrekt ist, muss den Einsteiger aber nicht wirklich interessieren.

Manche nennen sie auch Modul. Dies versuche ich zuvermeiden, da es in Contao Frontend- und Backendmodule gibt. Dies führt häufig zu Verwechselungen.

Kommandozeile oder Manager

Viele Operationen (wie das Leeren des Caches oder die Installation von Paketen) sind per Kommandozeile und über den Contao Manager möglich. Aus meiner Sicht kommt man beim Entwickeln nicht an der Kommandozeile vorbei, ich werde in meinen Texten deshalb diese Methode verwenden. Da der Manager relativ selbst erklärend ist, sollte die Umsetzung der einzelnen Schritte dort auch für Einsteiger ohne Erklärung möglich sein.

Überblick

app
├── config
│   └── parameters.yml
├── ContaoManagerPlugin.php
src
└── Ctocb
    └── Example
        ├── Classes
        │   ├── Contao
        │   │   └── Manager
        │   │       └── Plugin.php
        │   ├── Events
        │   │   └── OnDoSomethingEvent.php
        │   ├── Listener
        │   │   └── OnDoSomethingListener.php
        │   └── Services
        │       └── Helper
        │           └── TestToken.php
        ├── Resources
        │   ├── config
        │   │   ├── listener.yml
        │   │   └── services.yml
        │   └── contao
        │       ├── config
        │       │   └── config.php
        │       ├── dca
        │       │   └── tl_test.php
        │       ├── languages
        │       │   └── de
        │       │       └── tl_test.php
        │       └── templates
        │           └── ce_test.html5
        ├── .editorconfig
        ├── .gitignore
        ├── CtocbExampleBundle.php
        ├── README.md
        └── composer.json
composer.json

Ich werde auf die wichtigsten Dateien nun genauer eingehen. Die Standarddateien von Contao (im Ordner Resources/contao) sollten hinreichend bekannt sein und werden in den Abschnitten zu den jeweiligen Themen erläutert. Die restlichen Dateien der Contao-Installation wurden zur besseren Übersicht ausgeblendet.

Speicherort der Erweiterung

Wenn ich eine Erweiterung entwickle, liegt diese unter src/Ctocb/PROJEKT/. PROJEKT wird durch den Namen der Erweiterung ersetzt. Hierauf zeigt dann auch der psr-4-Autoload-Eintrag in der composer.json (s. nächster Abschnitt).

Im weiteren Verlauf werde ich das Projekt Example nennen. Wo immer dies steht, muss es durch den Namen des jeweiligen Projekts ersetzt werden!

Das Ctocb ist mein Vendor-Namespace. Ihr müsst dies bitte durch Euren eigenen Namespacce ersetzen. Dies kann ein Bezeichner für Eure Firma oder Euren Namen sind. Bitte NICHT Ctocb verwenden! Danke! (Wo auch immer Ctocb in einem Namen oder Namespace steht, dies bitte durch Euren eingnen Namespace oder Pfad ersetzten.)

composer.json

Es gibt die Datei composer.json zwei Mal. Die erste ist die von Contao mitgebrachte. Sie konfiguriert die Entwicklungsumgebung. Die zweite ist in unserer Erweiterung und konfiguriert die Abhängigkeiten und das Autoloading unserer Erweiterung, nach der Installation in beim Benutzer.

composer.json der Entwicklungsumgebung

Damit das Manager Plugin (und durch dies auch unser Bundle) in der Entwicklungsumgebung gefunden wird, muss in den Abschnitt autoload der Datei composer.json im Wurzelverzeichnis von Contao folgendes eingetragen werden:

"autoload": {
    "classmap": [
        "app/ContaoManager/Plugin.php"
    ],
    "psr-4": {
        "Ctocb\\": "src/Ctocb/"
    }
}

Der Eintrag classmap sorgt dafür, dass unser Plugin gefunden wird. Mit psr-4 konfigurieren wir den Autoload für die Klassen unseres Bundles. In diesem Fall liegen die Dateien im Ordner src/Ctocb/ und der Teil des Namespaces Ctocb\\ wird im Pfad dann durch src/Ctocb/ ersetzt. Alles was folgt, wird auf die Unterordner gemappt. Hat eine Datei also den Namespace Ctocb\Example\Classes\Contao\Manager und den Namen Plugin liegt sie unter /src/Ctocb/Example/Classes/Contao/Manager/Plugin.php.

Auch hier ist das Ctocb ist mein Vendor-Namespace. Ihr müsst dies bitte durch Euren eigenen Namespacce ersetzen.

composer.json der Erweiterung

In der composer.json der Erweiterung muss als type entweder contao-provider für die Installation als Artefakt über den Manager, oder contao-bundle bei einer Installation über ein VCS.

Im Abschnitt require stehen erst einmal PHP und Contao. Später werden ggf. weitere Pakete eingefügt, wenn wir weitere Abhängigkeiten benötigen. Wichtig ist, dass diese zwar über composer in die Entwicklungsumgebung installiert werden können, aber immer nur in der globalen composer.json der Installation eingetragen werden. In unserer Erweiterung müssen die Abhängigkeiten manuell gepflegt werden!

Neben dem PSR-4 Eintrag für das Autloading, wird hier auch unter extra das ManagerPlugin bekannt gegeben.

{
    "name": "Ctocb/Example",
    "description": "Es handelt sich um eine Erweiterung für das Open Source CMS Contao",
    "license": "proprietary",
    "type": "contao-bundle",
    "authors": [
        {
            "name": "Patrick Froch",
            "email": "info@easySolutionsIT.de",
            "homepage": "http://easySolutionsIT.de",
            "role": "Developer"
        }
    ],
    "support": {
        "email": "info@easySolutionsIT.de"
    },
    "require": {
        "php": "^8.1",
        "contao/manager-bundle": "^4.13"
    },
    "require-dev": {
        "contao/test-case": "^4.13",
        "phpunit/phpunit": "^9.5"
    },
    "autoload": {
        "psr-4": {
            "Ctocb\\Example\\": ""
        }
    },
    "extra": {
        "contao-manager-plugin": "Ctocb\\Example\\Classes\\Contao\\Manager\\Plugin"
    }
}

Auch hier bitte alle Vorkommen von Ctocb durch Euren eigenen Namespacce ersetzen. Ab jetzt werde ich es nicht mehr erwähnen, ich hoffe das Prinzip ist klar. ;)

Zwei Plugins für ein Bundle

Etwas unschön ist, dass wir für unsere Bundles auch zwei Plugins benötigen. Eins für die Entwicklungsumgebung und eins, das mit der Erweiterung ausgeliefert wird. Leider ist dies zurzeit nicht anders möglich.

Erweiterung

Das Manger Plugin der Erweiterung liegt bei mir in /src/Ctocb/Example/Classes/Contao/Manager und trägt den Namen Plugin.php. Die Details des Manager Plugins werden im nächsten Kapitel besprochen, sodass ich hier nur ein Beispiel zeige:

<?php declare(strict_types = 1);
namespace Ctocb\Example\Classes\Contao\Manager;

use Contao\CoreBundle\ContaoCoreBundle;
use Contao\ManagerPlugin\Bundle\BundlePluginInterface;
use Contao\ManagerPlugin\Bundle\Config\BundleConfig;
use Contao\ManagerPlugin\Bundle\Parser\ParserInterface;
use Contao\ManagerPlugin\Config\ConfigPluginInterface;
use Ctocb\Example\CtocbExampleBundle;

class Plugin implements BundlePluginInterface, ConfigPluginInterface
{

    public function getBundles(ParserInterface $parser)
    {
        return [BundleConfig::create(CtocbExampleBundle::class)->setLoadAfter([ContaoCoreBundle::class])];
    }

    public function registerContainerConfiguration(ParserInterface $parser)
    {
        $path = '@CtocbExampleBundle/Resources/config';
        $loader->load("$path/services.yml");
    }
}

Entwicklungsumgebung

Im Contao Handbuch steht, dass das Plugin unter \App\ContaoManager\Plugin.php liegen soll. Trotz intensiver Versuche funktioniert dies bei mir nicht. Die Klasse wird bei mir nur geladen, wenn Sie ContaoManagerPlugin heißt. Vielleicht habe ich es aber auch falsch verstanden. Ich gehe erst einmal weiter den Weg, dass ich das Manager Plugin unter app/ContaoManagerPlugin.php speichere.

Diese Klasse enthält keine Logik, sie erbt einfach vom Manager Plugin unserer Erweiterung.

<?php declare(strict_types=1);

use Ctocb\Example\Classes\Contao\Manager\Plugin;

class ContaoManagerPlugin extends Plugin {}

Bundle

Die Bundle-Klasse muss im Wurzelverzeichnis der Erweiterung liegen (/src/Ctocb/Example/CtocbExampleBundle.php) und enthält ebenfalls meist keine Logigk.

<?php declare(strict_types=1);

namespace Ctocb\Example;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class CtocbExampleBundle extends Bundle {}

Konfiguration für Contao

Die Konfigurationen sind seit Contao 3 eigentlich unverändert geblieben und liegen im Ordner /src/Ctocb/Example/Resources/contao. Wie gesagt, werde ich diese Dateien in den jeweiligen Kapiteln erläutern.

Symfony Dateien

Auf die Dateien, die für Symfony eine Rolle spielen (wie Events, Listener, Services oder die YML-Dateien), werde ich auch in den jeweiligen Kapiteln eingehen. Sie wurden im Listing weiter oben nur gezeigt, um einen groben Überblick über die Systematik der Aufteilung zu geben.