JTL Shop Plugin entwickeln – Praxisleitfaden (mit PHP-Beispielen)
Für wen ist dieser Artikel? Für JTL Shop-Entwickler:innen, die ein robustes, wartbares und wiederverwendbares Plugin-Setup suchen.
Überblick
In diesem Leitfaden entwickeln Sie ein JTL Shop Plugin mit produktionsreifen Mustern:
- Skalierbare Projektstruktur
- Manifeste für JTL Shop 4/5
- Hooks registrieren & sauber kapseln
- Admin-Einstellungen inkl. Validierung
- Template-Integration (Smarty)
- Datenbank-Migrationen
- Versionierung & Deployment
Alle Beispiele sind in PHP. Bei Versionsunterschieden weise ich explizit darauf hin.
Voraussetzungen
- PHP 7.4+ (abhängig von Ihrer JTL Shop Version)
- Zugriff auf eine JTL Shop Entwicklungs-/Staging-Umgebung
- Composer (optional, empfohlen)
- Grundkenntnisse in Smarty
Projektstruktur
So bleibt Ihr Plugin auch in Teams und CI/CD gut wartbar:
my-jtl-plugin/
├─ src/
│ ├─ Plugin.php
│ ├─ Services/
│ ├─ Hooks/
│ ├─ Admin/
│ └─ Templates/
├─ templates/
├─ migrations/
├─ vendor/
├─ plugin.json # JTL Shop 5 (bevorzugt)
├─ info.xml # JTL Shop 4 (nur wenn nötig)
├─ config.xml # Admin-Einstellungen
├─ README.md
└─ composer.json
plugin.jsonfür JTL Shop 5 verwenden.info.xmlnur für Alt-Installationen (v4).
Manifest (JTL Shop 5: plugin.json)
{
"name": "my-jtl-plugin",
"displayName": "My JTL Plugin",
"version": "1.0.0",
"author": "Ihr Unternehmen",
"url": "https://ideployed.com",
"minShopVersion": "5.0.0",
"description": "Fügt Feature X zum JTL Shop hinzu.",
"hooks": [
{ "id": 0, "file": "src/Hooks/OnPageLoaded.php" },
{ "id": 99, "file": "src/Hooks/BeforeCheckout.php" }
],
"adminMenu": [
{ "name": "My Plugin", "url": "plugin.php?plugin=my-jtl-plugin", "rights": ["VIEW"] }
],
"install": "src/Plugin.php",
"uninstall": "src/Plugin.php",
"update": "src/Plugin.php"
}
hljs jsonHook-IDs sind versionsabhängig. Vermeiden Sie „Magic Numbers“ und kapseln Sie IDs in Konstanten. Prüfen Sie die offizielle Hook-Liste Ihrer Zielversion.
Legacy (JTL Shop 4: info.xml)
<?xml version="1.0" encoding="UTF-8"?>
<jtlshopplugin>
<Name>My JTL Plugin</Name>
<Author>Ihr Unternehmen</Author>
<Version>1.0.0</Version>
<MinShopVersion>4.0.0</MinShopVersion>
<Hooks>
<Hook>
<HookID>0</HookID>
<File>src/Hooks/OnPageLoaded.php</File>
</Hook>
</Hooks>
</jtlshopplugin>
hljs xmlBootstrap (src/Plugin.php)
Schlanker, testbarer Lebenszyklus:
<?php
declare(strict_types=1);
namespace MyJtlPlugin;
final class Plugin
{
public static function install(): void
{
self::runMigrations(__DIR__ . '/../migrations');
}
public static function update(string $fromVersion, string $toVersion): void
{
self::runMigrations(__DIR__ . '/../migrations');
}
public static function uninstall(): void
{
// Daten nach Möglichkeit behalten (Opt-In für Hard-Delete)
}
private static function runMigrations(string $dir): void
{
foreach (glob($dir . '/*.sql') as $file) {
$sql = file_get_contents($file);
if ($sql !== false && trim($sql) !== '') {
self::db()->exec($sql);
}
}
}
private static function db(): \PDO
{
return new \PDO('mysql:host=localhost;dbname=shop','user','pass', [
\PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
]);
}
}
hljs phpHooks registrieren (Beispiele)
src/Hooks/OnPageLoaded.php — sicher mit Smarty arbeiten:
<?php
declare(strict_types=1);
namespace MyJtlPlugin\Hooks;
final class OnPageLoaded
{
public function __invoke(array $args): void
{
$smarty = $args['smarty'] ?? null;
if ($smarty instanceof \Smarty) {
$smarty->assign('MY_PLUGIN_GREETING', 'Hallo vom My JTL Plugin!');
}
}
}
hljs phpsrc/Hooks/BeforeCheckout.php — Beispiel für Checkout-Validierung:
<?php
declare(strict_types=1);
namespace MyJtlPlugin\Hooks;
final class BeforeCheckout
{
public function __invoke(array $args): void
{
$cart = $args['cart'] ?? null;
if ($cart && $this->hasDisallowedItems($cart)) {
$smarty = $args['smarty'] ?? null;
if ($smarty instanceof \Smarty) {
$smarty->assign('MY_PLUGIN_CHECKOUT_WARNING', 'Bestimmte Artikel erfordern eine manuelle Prüfung.');
}
}
}
private function hasDisallowedItems($cart): bool
{
return false;
}
}
hljs phpHalten Sie Ihre Hook-Handler idempotent und performant. Das verhindert doppelte Ausführung und unnötige Seiteneffekte.
Admin-Einstellungen (config.xml)
Funktionen ein-/ausschalten und Werte speichern:
<?xml version="1.0" encoding="utf-8"?>
<config>
<section key="my_jtl_plugin" label="My JTL Plugin">
<setting key="enabled" type="selectbox" label="Plugin aktivieren">
<option value="0">Deaktiviert</option>
<option value="1" selected="selected">Aktiviert</option>
</setting>
<setting key="greeting_text" type="text" label="Begrüßungstext" default="Hallo vom My JTL Plugin!" />
</section>
</config>
hljs xmlIm Hook lesen:
$enabled = (int)($_ENV['MY_JTL_PLUGIN_ENABLED'] ?? 1);
$greeting = $_ENV['MY_JTL_PLUGIN_GREETING'] ?? 'Hallo vom My JTL Plugin!';
hljs phpNutzen Sie in Produktion die offizielle Konfig-Schnittstelle Ihrer JTL-Version statt direktem Zugriff auf
$_ENV.
Template-Integration (Smarty)
Kleine Komponente einblenden:
templates/snippets/my_plugin_banner.tpl
{if $MY_PLUGIN_GREETING} <div class="alert alert-info my-plugin-banner"> {$MY_PLUGIN_GREETING|escape} </div> {/if}hljs smarty
Einbinden wo nötig:
{include file="snippets/my_plugin_banner.tpl"}hljs smarty
Checkout-Warnungen:
templates/snippets/my_plugin_checkout_warning.tpl
{if $MY_PLUGIN_CHECKOUT_WARNING} <div class="alert alert-warning my-plugin-warning"> {$MY_PLUGIN_CHECKOUT_WARNING|escape} </div> {/if}hljs smarty
Datenbank-Migrationen
migrations/001_create_flags.sql
CREATE TABLE IF NOT EXISTS my_plugin_flags (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(64) NOT NULL,
enabled TINYINT(1) NOT NULL DEFAULT 1,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
hljs sqlmigrations/002_add_description.sql
ALTER TABLE my_plugin_flags
ADD COLUMN description VARCHAR(255) NULL AFTER name;
hljs sqlNummerierte Einzeldateien helfen, nur notwendige Migrationen beim Update auszuführen.
Versionierung & Kompatibilität
- SemVer verwenden (
MAJOR.MINOR.PATCH) minShopVersionim Manifest aktuell halten- CHANGELOG.md pflegen
- Bei mehreren Major-Versionen: Kompatibilitätsschichten kapseln
Beispiel CHANGELOG.md:
## 1.1.0 - 2025-06-10
- Checkout-Warnung hinzugefügt (konfigurierbar)
- Feature-Flag-Tabelle eingeführt (Migration 001)
- Getestet mit JTL Shop 5.2.x
Test-Checkliste
- ✅ Hooks sind idempotent
- ✅ Admin-Einstellungen validieren/speichern korrekt
- ✅ Keine Smarty-Konflikte
- ✅ Staging-Test mit realistischen Daten
- ✅ Rollback möglich
- ✅ Performance unter Produktionslast getestet
Nutzen Sie reale Testdaten im Staging, um frühzeitig Probleme zu erkennen.
Deployment
- Nur notwendige Artefakte packen
- Keine Secrets im Repo – Umgebungsvariablen nutzen
- Staging ≈ Produktion
- Automatisches Deployment bevorzugen
Beispiel Build-Skript:
#!/usr/bin/env bash
set -euo pipefail
PLUGIN_NAME="my-jtl-plugin"
OUT="dist/${PLUGIN_NAME}.zip"
rm -f "$OUT"
mkdir -p dist
zip -r "$OUT" src/ templates/ migrations/ plugin.json info.xml config.xml README.md -x "**/.DS_Store" "**/node_modules/**" "**/vendor/**"
hljs bash⚡️ Ein-Klick-Deployment mit iDeployed. Mit iDeployed erstellen Sie isolierte Staging-Shops, testen Plugins mit produktionsnahen Daten und deployen sicher. Jetzt kostenlos testen →