Migration von Drupal zu TYPO3: Eine Schritt-für-Schritt-Anleitung

Migration von Drupal zu TYPO3: Eine Schritt-für-Schritt-Anleitung

Kontrollieren Sie Ihr Datenmodell und Ihre URLs, und die Migration bleibt einfach. Dieses Handbuch bietet einen praktischen Weg von Drupal 11 zu TYPO3 13, saubere JSON/CSV-Exporte, präzise Zuordnungen, Twig→Fluid-Anleitungen, sichere 301-Weiterleitungen und wiederholbare Importe, denen Sie vertrauen können.

Schnelle Checkliste (konzeptionell)

  • Drupal-Entitäten → TYPO3-Datensätze und endgültige Slugs zuordnen.
  • TYPO3 13 Setup auswählen (Sites, Sprachen, Seitenbaum).
  • Export über JSON:API (oder CSV über Views); Dateien kopieren.
  • HTML/Assets normalisieren; Templates angleichen (Twig→Fluid).
  • Kategorien, Seiten/Datensätze, Medien und 301er importieren.
  • Anzahl, Links, SEO überprüfen; live gehen.
  • Quelle: Drupal 11 (gleicher Ablauf funktioniert für 9/10).
  • Ziel: TYPO3 13 LTS (empfohlen).
  • Einmal entscheiden: Seiten & Sprachen, Seitenbaum-Wurzeln und ob große Inhaltstypen zu Datensätzen (z.B. News/benutzerdefinierte TCA) oder Seiten werden. Alles andere folgt.
DrupalBeispielTYPO3 ZielHinweise
Node: Basisseite/aboutSeite + tt_contentStatischer Inhalt als Text-/Bildelemente.
Node: Artikel/Blog/blog/fooDatensatz (z.B. EXT:news) oder SeiteBevorzugen Sie Datensätze für Listen/Filter.
Taxonomiebegriff„Veranstaltungen“sys_categoryBehalten Sie Slugs bei; beziehen Sie sich auf Seiten/Datensätze.
BenutzerRedakteurebe_users / be_groups (Redakteure); fe_users / fe_groups (Seitenbenutzer)Trennen Sie Back-Office und Front-Office.
Datei/sites/default/files/*FAL (sys_file + sys_file_reference)Behalten Sie Dateinamen/Ordner zur Rückverfolgbarkeit bei.

Verwenden Sie die Migration, um den Umfang zu reduzieren. Markieren Sie nur das, was Sie wirklich in TYPO3 benötigen.

FunktionPrüfenTYPO3-Fähigkeit
PersonalisierungÜber Erweiterungen/Integrationen
eCommerceext:aimeos oder ext:commerce
Multi-SiteNative Sites-Unterstützung
Rollen/BerechtigungenGranulare BE/FE-Gruppen
HeadlessEXT:headless/benutzerdefinierte APIs
MehrsprachigkeitNatives Mehrsprachensystem
WorkflowWorkspaces (Entwurf/Überprüfung/Versionierung)
MedienFAL mit Speicher/Metadaten
Formulareext:form mit Editor
BarrierefreiheitWCAG-freundliche Ausgabe (Templating)
CLIKern und helhum/typo3-console

A) JSON:API (empfohlen und wiederholbar)

 

  • Inhalt: /jsonapi/node/{bundle} (z.B. Seite, Artikel)
    • Verwenden Sie ?include=field_image,uid,field_tags, um Beziehungen abzurufen.
    • Verwenden Sie ?page[limit]=N für die Paginierung; exportieren Sie pro Sprache mit filter[langcode]=….
  • Taxonomie: /jsonapi/taxonomy_term/{vocabulary}
  • Dateien: /jsonapi/file/file → verwenden Sie attributes.uri.url für die Download-URL.
  • Aliase (für 301s): /jsonapi/path_alias/path_alias → behalten Sie Quelle, Alias, langcode.

B) Ansichten → CSV (abgestimmt auf Ihren Importeur in §6B)

  • Erstellen Sie eine Drupal-Ansicht pro Entität (Seiten, Artikel, Kategorien, Benutzer, Weiterleitungen, Dateien).
  • Fügen Sie stabile Spalten ein: external_id (UUID), Slugs, Titel, HTML-Felder, Daten, Autor-E-Mail, Kategorie-Slugs, Bild-URL(s), Alias-Quellen/Ziele.
  • Halten Sie die Spaltennamen konsistent mit Ihrem TYPO3 CSV-Importeur, um Upserts und Wiederholungen zu ermöglichen. (Kein Code hier—nur die Spezifikationsausrichtung.)
Drupal-RolleTYPO3-Äquivalent
AdministratorBackend-Admin-Benutzer
Authentifizierter BenutzerFrontend-Benutzer (fe_users)
AnonymAnonymer Frontend-Zugang
Benutzerdefinierte RollenBenutzerdefinierte BE/FE-Gruppen
  • HTML: Entfernen Sie Drupal-spezifische Filter; konvertieren Sie Einbettungen in reines HTML oder geplante Inhaltselemente.
  • Medien: Kopieren Sie Binärdateien nach fileadmin/migrated/...; behalten Sie die Originaldateinamen bei; bereiten Sie eine alte-URL → neuer-Pfad-Karte vor.
  • Taxonomien: Erstellen Sie sys_category aus dem Export vor; verwenden Sie Slugs, wo möglich.
  • Benutzer: Importieren Sie nur benötigte Redakteure/Seitenbenutzer; ordnen Sie sie BE/FE-Gruppen zu.

Drupal Twig → TYPO3 Fluid Grundlagen

  • Layouts/Partials: Twig {% include %} → Fluid .
  • Schleifen/Bedingungen: Twig {% for %} / {% if %} → Fluid / .
  • URLs/Assets: Ersetzen Sie file_url() und feste Pfade durch FAL-gesteuerte Helfer und -View-Helper.
  • Menüs: Drupal-Menüs → TYPO3-Menü-Rendering aus dem Seitenbaum (TypoScript DataProcessors + Fluid).
  • Übersetzungen: Halten Sie Sprachschlüssel konsistent; TYPO3 website Sprache muss existieren, bevor Übersetzungen importiert werden.

A) JSON → TYPO3 (kompaktes PHP, ausgerichtet auf Drupal JSON:API)

Beispiel Drupal JSON

{
  "data": [{
    "type": "node--article",
    "id": "b5c1-uuid",
    "attributes": {
      "title": "Mein Artikel",
      "body": { "value": "<p>HTML…</p>", "format": "basic_html" },
      "created": "2025-05-01T10:20:30+00:00",
      "langcode": "de",
      "path": { "alias": "/blog/mein-artikel" }
    },
    "relationships": {
      "field_image": { "data": [{ "type": "file--file", "id": "f-uuid" }] },
      "field_tags":  { "data": [{ "type": "taxonomy_term--tags", "id": "t-uuid" }] }
    }
  }],
  "included": [{
    "type": "file--file",
    "id": "f-uuid",
    "attributes": {
      "filename": "hero.jpg",
      "uri": { "url": "https://example.com/sites/default/files/hero.jpg" }
    }
  }]
}

Abrufen & paginieren (auf Festplatte zwischenspeichern)

funktion fetchDocuments(string $url): array {
  $out = [];
  while ($url) {
    $json = json_decode(file_get_contents($url), true);
    if (!is_array($json)) break;
    $out[] = $json;
    $url = $json['links']['next']['href'] ?? null; // JSON:API Paginierung
  }
  return $out;
}
// Beispiel: Artikel mit Includes
$docs = fetchDocuments('https://drupal.example/jsonapi/node/article?filter[langcode]=en&include=field_image,uid,field_tags&page[limit]=50');
file_put_contents('stage/articles_en.json', json_encode($docs, JSON_PRETTY_PRINT|JSON_UNESCAPED_SLASHES));

Import Mapping (Konzept-Ebene Helfer; in TYPO3 ausführen)

$docs = json_decode(file_get_contents('stage/articles_en.json'), true);

function indexIncluded(array $doc): array {
  $files = $terms = [];
  foreach (($doc['included'] ?? []) as $res) {
    if ($res['type'] === 'file--file') {
      $files[$res['id']] = [
        'uuid' => $res['id'],
        'filename' => $res['attributes']['filename'] ?? '',
        'url' => $res['attributes']['uri']['url'] ?? null,
      ];
    } elseif (str_starts_with($res['type'], 'taxonomy_term--')) {
      $terms[$res['id']] = [
        'uuid' => $res['id'],
        'name' => $res['attributes']['name'] ?? '',
        'slug' => $res['attributes']['path']['alias'] ?? null
      ];
    }
  }
  return [$files, $terms];
}

foreach ($docs as $doc) {
  [$fileIndex, $termIndex] = indexIncluded($doc);

  foreach ($termIndex as $t) {
    upsertCategory($t['uuid'], ['title' => $t['name'], 'slug' => $t['slug'] ?: slugify($t['name'])]);
  }

  foreach ($doc['data'] as $node) {
    if ($node['type'] !== 'node--article') continue;

    $uuid  = $node['id'];
    $attr  = $node['attributes'];
    $rels  = $node['relationships'] ?? [];

    $title = $attr['title'] ?? '';
    $body  = $attr['body']['value'] ?? '';
    $lang  = $attr['langcode'] ?? 'en';
    $alias = $attr['path']['alias'] ?? null;
    $slug  = $alias ?: '/'.slugify($title);

    $pageUid = upsertPage($uuid, [
      'pid' => 0,
      'title' => $title,
      'slug' => $slug,
      'sys_language_uid' => langUid($lang),
    ]);

    insertTextElement($pageUid, normalizeHtml($body), $title);

    foreach ((array)($rels['field_image']['data'] ?? []) as $imgRef) {
      $fileMeta = $fileIndex[$imgRef['id']] ?? null;
      if (!$fileMeta || empty($fileMeta['url'])) continue;
      $fileUid = ensureFalFromUrl($fileMeta['url'], $fileMeta['filename']);
      attachImageToPage($pageUid, $fileUid);
    }

    foreach ((array)($rels['field_tags']['data'] ?? []) as $ref) {
      relatePageToCategory($pageUid, externalIdToCategoryUid($ref['id']));
    }

    if ($alias) rememberRedirect($alias, $slug, 301);
  }
}

flushRememberedRedirects();

/* Helper-Signaturen, die Sie mit TYPO3-APIs implementieren werden (DataHandler + FAL):
upsertCategory($externalId, $data): int
upsertPage($externalId, $data): int
insertTextElement($pid, $html, $header): int
ensureFalFromUrl($url, $filename): int
attachImageToPage($pageUid, $fileUid): void
relatePageToCategory($pageUid, $categoryUid): void
externalIdToCategoryUid($externalId): ?int
langUid($langcode): int
slugify($text): string
normalizeHtml($html): string
rememberRedirect($source, $target, $status): void
flushRememberedRedirects(): void
*/

B) CSV-Pfad mit T3Planet Migration Extension

Wenn Sie Drupal in CSVs exportieren können, die dem von T3Planet’s „Migrate WordPress to TYPO3“ verwendeten WordPress-Schema ähneln, können Sie dieses Tool auch für Drupal verwenden. Es ist ideal für Nicht-Entwickler-Teams und wiederholbare Trockenläufe.

Drupal-FunktionTYPO3 ExtensionSchlüssel
Nachrichten/BlogNachrichtensystemext:news
SEO (Meta/Sitemap)On-page SEOext:seo (Kern)
FormulareFormular-Frameworkext:form
NewsletterDirektmail / Integrationenext:direct_mail
eCommerceAimeosext:aimeos
WorkflowArbeitsbereicheKern
Drush-ähnliche CLITYPO3-KonsoleKern helhum/typo3-console

Halten Sie es vorhersehbar: stabile UUIDs, sauberes JSON/CSV, FAL für Medien und 301s für jeden Alias. Entscheiden Sie sich frühzeitig für Datensätze vs. Seiten, spiegeln Sie Kategorieschnecken und führen Sie Importe idempotent aus, bis die Differenz null ist. Dann versenden.

Ihre zentrale Lösung für individuelle TYPO3-Entwicklung

Entdecken Sie individuelle TYPO3-Entwicklungslösungen im T3Planet Shop – abgestimmt auf Ihr Projekt, Ihre Geschäftsziele und Ihre technischen Anforderungen.

  • Über ein Jahrzehnt TYPO3-Erfahrung
  • 350+ erfolgreiche TYPO3-Projekte
  • 87 % wiederkehrende TYPO3-Kunden
TYPO3 Service
wolfgang weber

Post a Comment

×