Contao-Update: CSS-Klassen in MySQL automatisiert ersetzen
Anleitung für Contao 5.3
Das Stylesheet ist umgestellt, alle .ce_-Klassen heißen jetzt .content- – und trotzdem sieht die Hälfte der Seiten kaputt aus. Der Grund versteckt sich eine Ebene tiefer: Klassennamen leben nicht nur in CSS-Dateien, sondern auch in der MySQL-Datenbank – in Inhaltselementen, Modulen, Layouts und Formularfeldern. Wer sie dort von Hand nachzieht, klickt sich durch hunderte Datensätze und übersieht garantiert welche.
Der bessere Weg: die Ersetzung automatisieren – per PHP-Skript oder direkt mit gezielten SQL-Befehlen. Wie das sicher funktioniert, inklusive Trockenlauf und Rückweg, zeigt dieser Beitrag.
Tabellen und Spalten mit gespeicherten Klassennamen
Klassennamen können in unterschiedlichen Tabellen und Spalten gespeichert sein. Häufig betroffen sind:
tl_content(z. B. Feldtextoderrsce_data)tl_module(z. B. Felderhtml,cssID)tl_layout(z. B. FeldcssClass)tl_form_field(z. B. Feldclass)
Besonders bei den Rocksolid Custom Elements ist Vorsicht geboten, da deren Konfiguration in einem Blob gespeichert wird. Dennoch ist ein automatisches Ersetzen im Feld rsce_data möglich.
Automatisierte Ersetzung mit PHP
Das folgende Skript stellt eine Verbindung zur Datenbank her und ersetzt definierte Werte in den entsprechenden Tabellen und Spalten.
<?php
// Datenbankverbindung herstellen (Zugangsdaten anpassen)
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "database";
$conn = new mysqli($servername, $username, $password, $dbname);
// Fehlerprüfung bei der Verbindung
if ($conn->connect_error) {
die("Verbindung fehlgeschlagen: " . $conn->connect_error);
}
// Liste der zu ersetzenden Werte und deren neuen Werte
$replacements = array(
array('table' => 'tl_content', 'column' => 'text', 'old_value' => 'css_klasse', 'new_value' => 'css-klasse'),
array('table' => 'tl_module', 'column' => 'html', 'old_value' => 'css_klasse', 'new_value' => 'css-klasse'),
array('table' => 'tl_module', 'column' => 'cssID', 'old_value' => 'css_klasse', 'new_value' => 'css-klasse'),
array('table' => 'tl_layout', 'column' => 'cssClass', 'old_value' => 'css_klasse', 'new_value' => 'css-klasse'),
array('table' => 'tl_form_field', 'column' => 'class', 'old_value' => 'css_klasse', 'new_value' => 'css-klasse'),
array('table' => 'tl_content', 'column' => 'rsce_data', 'old_value' => 'css_klasse', 'new_value' => 'css-klasse')
);
// Durchlaufen der Liste und Ausführen der Aktualisierungen
foreach ($replacements as $replacement) {
$table = $replacement["table"];
$column = $replacement["column"];
$old_value = $replacement["old_value"];
$new_value = $replacement["new_value"];
$sql = "UPDATE `$table` SET `$column` = REPLACE(`$column`, '$old_value', '$new_value')";
if ($conn->query($sql) === TRUE) {
echo "Tabelle '$table', Spalte '$column': '$old_value' erfolgreich ersetzt durch '$new_value'<br>";
} else {
echo "Fehler beim Aktualisieren von '$table', Spalte '$column': " . $conn->error . "<br>";
}
}
// Verbindung schließen
$conn->close();
?>
Vorteile dieser Methode
- Alle relevanten Tabellen und Spalten lassen sich in einer zentralen Liste definieren.
- Die Ersetzungen erfolgen automatisiert und einheitlich.
- Auch komplexere Felder wie
rsce_datain Rocksolid Custom Elements können berücksichtigt werden.
Update 2026: sicher ersetzen – mit Trockenlauf und Wortgrenzen
Update 2026: Direkte UPDATE-Befehle auf der Datenbank sind schnell – und unbarmherzig. Ein vergessenes WHERE oder ein zu gieriges Suchmuster richtet in Sekunden Schaden an, den nur ein Backup heilt. Diese Variante arbeitet mit Trockenlauf, exakten Wortgrenzen und Rückweg.
Schritt 1: Backup – ohne Ausnahme
mysqldump -u USER -p DATENBANK > vor-klassenumbau-$(date +%F-%H%M).sqlSchritt 2: Trockenlauf – erst sehen, was passieren würde
-- Wie viele Datensätze wären betroffen? (nichts wird verändert)
SELECT id, type, LEFT(cssID, 120) AS vorher
FROM tl_content
WHERE cssID REGEXP '(^|[^a-z0-9_-])alt-klasse([^a-z0-9_-]|$)'
LIMIT 50;
-- Zähler für das Protokoll
SELECT COUNT(*) AS treffer FROM tl_content
WHERE cssID REGEXP '(^|[^a-z0-9_-])alt-klasse([^a-z0-9_-]|$)';Der Ausdruck mit den Wortgrenzen ist der entscheidende Unterschied zu einem einfachen LIKE '%alt-klasse%': Ohne ihn wird auch alt-klasse-2 oder meine-alt-klasse getroffen – und genau daraus entstehen die Fehler, die erst Wochen später auffallen.
Schritt 3: Ersetzen – mit exakter Wortgrenze
-- MySQL 8 / MariaDB 10.5+: REGEXP_REPLACE
UPDATE tl_content
SET cssID = REGEXP_REPLACE(
cssID,
'(^|[^a-z0-9_-])alt-klasse([^a-z0-9_-]|$)',
'\\1neue-klasse\\2'
)
WHERE cssID REGEXP '(^|[^a-z0-9_-])alt-klasse([^a-z0-9_-]|$)';
-- Ältere Versionen ohne REGEXP_REPLACE: gezielt und mit Leerzeichen-Puffer
UPDATE tl_content
SET cssID = TRIM(REPLACE(CONCAT(' ', cssID, ' '), ' alt-klasse ', ' neue-klasse '))
WHERE CONCAT(' ', cssID, ' ') LIKE '% alt-klasse %';Schritt 4: Nachkontrolle
-- Darf keine Treffer mehr liefern
SELECT COUNT(*) FROM tl_content
WHERE cssID REGEXP '(^|[^a-z0-9_-])alt-klasse([^a-z0-9_-]|$)';
-- Cache leeren, sonst zeigt das Frontend die alte Fassung
-- vendor/bin/contao-console cache:clear --env=prodNicht vergessen: Klassen stecken nicht nur in tl_content
tl_article,tl_moduleundtl_layoutführen ebenfalls CSS-Klassen.- In Rich-Text-Feldern (
tl_content.text) stehen Klassen mitten im HTML – dort hilft nur eine Suche mit Kontext, kein pauschales Ersetzen. - Templates und Stylesheets im Dateisystem sind von der Datenbank unberührt:
grep -rl "alt-klasse" templates/ files/zeigt, was noch offen ist.
Wer den Umbau in einem größeren Projekt macht, schreibt ihn besser als Contao-Migration – dann läuft er auf Staging und Live identisch, ist versioniert und lässt sich nachvollziehen. Ein Datenbank-Einzeiler auf der Live-Umgebung ist der schnellste Weg zu einem unerklärlichen Layoutfehler drei Wochen später.
Fazit
Die Datenbank ist beim Klassen-Umbau der Ort, an dem sich Gründlichkeit auszahlt – und Nachlässigkeit rächt. Ein automatisiertes Skript erledigt in Sekunden, was manuell Tage kostet; entscheidend ist die Reihenfolge: Backup, Trockenlauf, Ersetzung mit Wortgrenzen, Nachkontrolle. Wer diese vier Schritte einhält, kann dem UPDATE-Befehl gelassen zusehen.
Und wer den Umbau öfter als einmal braucht – Staging, Live, das nächste Projekt –, gießt ihn in eine Contao-Migration. Dann ist die Ersetzung kein riskanter Handgriff mehr, sondern ein dokumentierter, wiederholbarer Teil des Deployments.