Archiv der Kategorie: Datenimport

Drupal nach WordPress: Beiträge mit Kategorien erzeugen

Kurze Wiederholung: unsere Import-Tabelle sieht folgendermassen aus:

ersterwert_letzterwert
ersterwert_letzterwert

Dabei sind die Ersterwert/Letzterwert Felder die term_ids aus WordPress. Ich vegebe mal noch hübschere Feldnamen und hole zum Testen nur eine Handvoll Datensätze:

neue_feldnamen
neue_feldnamen

 

Wir bauen jetzt unser Import-Plugin entsprechend um. Der Select bleibt gleich, der Tabellenname liegt auf der Variablen $akt_import:

global $wpdb;
    $allezeilen = $wpdb->get_results( "SELECT * from ".$akt_import."");

Innerhalb der Foreach-Schleife konstruieren wir uns jetzt unsere neuen Beiträge:

foreach ($allezeilen as $zeile){
        
        $akt_titel = $zeile->title;
        $akt_body_value = $zeile->body_value;
        $akt_term_id1 = $zeile->term_id1;
        $akt_term_id2 = $zeile->term_id2;

        // Beitragsobjekt anlegen
        $my_post = array();
        $my_post['post_title']    = $akt_titel;
        $my_post['post_content']  = $akt_body_value;
        $my_post['post_status']   = 'publish';
        $my_post['post_author']   = 1;
        $my_post['post_category'] = array( $akt_term_id1,$akt_term_id2 );

        // Beitrag in Datenbank einfügen
        $neue_id = wp_insert_post( $my_post );
        echo "Beitrag mit neuer ID: ".$neue_id." angelegt <br><br>";
        
    } //end foreach

Erst wird das Array für das neue Beitragsobjekt mit den Datenfeldern aus der aktuellen Zeile gefüllt, und dann mit wp_insert_post der neue Beitrag angelegt. Das wars schon! Alle Rezepte mitsamt Kategorien werden erzeugt.

import_komplett
import_komplett

Natürlich muss man an der Logik noch ein bisschen rumschrauben, wenn man mehr als zwei Kategorien pro Rezept importieren will, aber prinzipiell funktioniert die Sache so, das geht eigentlich ganz unkompliziert, wenn man die Export-Tabelle einmal konstruiert hat.

Von Drupal nach WordPress: der Vollständigkeit halber

Es kann ja rein theoretisch mal vorkommen, dass man existierende Drupal-Inhalte nach WordPress übernehmen möchte. Gehen wir mal davon aus, dass wir einen bestimmten Inhalltstyp aus Drupal als Blog-Beiträge nach WordPress exportieren möchte. Wir bleiben beim Kochbuch, der relevante Inhaltstyp ist „rezept“. Ich hab mal im Archiv gegraben und mir die Mechanik fürs grundlegende Anlegen eines WordPress-Posts geholt:

WordPress Post anlegen

// Beitragsobjekt anlegen
    $my_post = array();
    $my_post['post_title']    = 'Mein Titel';
    $my_post['post_content']  = 'Mein Content';
    $my_post['post_status']   = 'publish';
    $my_post['post_author']   = 1;

    // Beitrag in Datenbank einfügen
    //$neue_id = wp_insert_post( $my_post );

Mehr brauchts eigentlich nicht für den Anfang, der Titel und der Inhalt reichen mal für Demozwecke (Die Kategorien kommen später dran). Jetzt suchen wir uns aus Drupal die relevanten Daten zusammen.

Relevante Tabellen in Drupal

Zum einen die node-Tabelle, aus der brauchen wir vor allem die nid, den type und den title. Der content steckt in der field_data_body im Feld body_value, verknüpft wird über die nid im Feld entity_id. Der SQL sieht so aus:

SELECT field_data_body.entity_id, field_data_body.body_value, node.nid, node.type, node.title
FROM field_data_body INNER JOIN node ON field_data_body.[entity_id] = node.[nid];

Das Ergebnis sieht erstmal so aus:

node_fields
node_fields

Wir haben hier noch articles und pages mit drin, die filtern wir mit einem where type = ‚rezept‘ weg, dann passt die Sache.

Jetzt fehlen noch die Kategorien

Dafür brauchen wir zuerstmal die Tabelle taxonomy_term_data, in der stehen die id und Namen der Kategorien drin, und die vid, das ist der Index der verwendeten Taxonomy, das ist bei mir die 2. Das ergibt bei mir eine Liste mit 28 Einträgen, von denen ich nur die tid und den Namen brauche:

tid_name
tid_name

Die importiere ich mit dem phpmyadmin in unsere WordPress-Datenbank.

Schritt 1: Anlegen der Kategorien in WordPress

Dafür setzen wir uns ein kleines Plugin auf, das zunächst nicht mehr als ein  Formular mit einem Textfeld für den Namen der zu importierenden Tabelle und einen Start-Button enthält. Der Kern des Plugins ist ganz einfach. Auf der Variablen $akt_import liegt der Name der einzulesenden Tabelle mit den Kategorienamen. Durch die steppe ich zeilenweise durch und lege mit wp_create_category() die Kategorien neu an:

global $wpdb;
    $allezeilen = $wpdb->get_results( "SELECT * from ".$akt_import."");
    
    foreach ($allezeilen as $zeile){
        
        echo $zeile->name."<br>";
        wp_create_category($zeile->name);
    }

Das Ergebnis ist wie erwartet:

kat_in_wp
kat_in_wp

28 neue Kategorien. Von denen brauchen wir jetzt die WordPress-Kategorie-IDs, die stecken jetzt in der Tabelle wp_terms:

wp_terms
wp_terms

Schritt 2: verknüpfen mit den Drupal-Daten

Die wp_terms hole ich mir jetzt zu den Drupal-Tabellen nach Access rein. Dort habe ich mir inzwischen aus der node und aus der field_data_body eine neue Tabelle gebaut, die nur die nid, den title und den body_value enthält:

nid_title_body_value
nid_title_body_value

Die wird jetzt erstmal über die nid mit der taxonomy_index und diese über die tid mit der taxonomy_term_data verknüpft:

nid_tid_verknuepfung
nid_tid_verknuepfung

Dann hole ich mir die wp_terms mit dazu und verknüpfe über den name  mit der taxonomy_term_data:

mit_wp_terms
mit_wp_terms

Damit haben wir alle Felder, die wir für den Export nach WordPress brauchen, die term_id liefert uns die richtige Kategorie:

nodes_mit_term_id
nodes_mit_term_id

Das specken wir noch ein wenig ab und basteln uns eine saubere Export-Tabelle. Die enthält zunächst nochmal jeden Datensatz so oft, wie er Kategorien zugeordnet hat. Da ich in Drupal nur zwei Kategorien pro Datensatz hatte, taucht hier also jedes Rezept zweimal auf. Darauf setze ich eine Abfrage mit Gruppierung und nehme von der term_id einmal den letzten Wert und einmal den ersten Wert:

ersterwert_letzterwert
ersterwert_letzterwert

So, das wars. Diese Abfrage exportieren wir uns als CSV und holen sie uns in unsere WordPress-Datenbank rein. Für den tatsächlichen Import gibts aber einen neuen Beitrag.

Meine Export-Datei, und wie man die Nodes jetzt tatsächlich anlegt

Ich hab mir die Tabelle taxonomy_term_data aus Drupal ins Access überspielt und mit meinen WordPress-Tabellen verknüpft, so komme ich an die tag-IDs. Das läuft genau wie bei der Erzeugung der Export-Datei in Joomla, nachzulesen hier in meinem Artikel über das Tag-Mapping. Das vorläufige Endergebnis habe ich mal auf 24 Rezepte beschränkt, aussehen tut es so:

24_rezepte
24_rezepte

ID, post_content, post_name und titel kommen aus WordPress, die tags 1..3 sind die gemappten tag-IDs aus Drupal. Den post_content hab ich schon bereinigt, da sind schon alle Links draussen und alle Caption-Shortcodes entfernt, wie das geht habe ich in diesem Artikel beschrieben.

Ich habs mal auf maximal drei Tags beschränkt, das reicht zum Vorführen. Diese Tabelle kommt jetzt in die Drupal-Database mit rein, und dann wollen wir mal.

Vorbereitungen

Voraussetzung für den Import ist, dass das Modul „Blogs“ aktiviert und ein Menüpunkt für die Blogseite erstellt ist, wie das geht hab ich in diesem Artikel beschrieben. Dann ändern wir in unserem Source den node type auf blog:

$node->type = 'blog';

Der Import mit db_query

Die Import-Tabelle lesen wir wie gehabt mit einem db_query ein, ich hab hier mal für den ersten Test noch eine where-Klausel mit drin, um die Sache erstmal auf einen Datensatz zu beschränken:

//***exporttabelle einlesen
        $query = db_query("SELECT * FROM 24_export where id = 205");
        $records = $query->fetchAll();

Mit einem Foreach() holen wir uns die einzelnen Zeilen der Query und legen uns die Inhalte auf Variable:

foreach ($records as $record) {
                
                $akt_titel = $record->titel;
                $akt_content = $record->post_content;
                $akt_alias = $record->post_name;
                $akt_tag1 = $record->tag1;

            // hier kommt die Action hin

}

Query-Ergebnisse in Variable einlesen

Diese Variablen passen wir dann in die Kreation des Nodes ein.

$body_text = $akt_content;

                $node = new stdClass();
                $node->type = 'blog';
                node_object_prepare($node);

                $node->title    = $akt_titel;
                $node->language = LANGUAGE_NONE;

                $node->body[$node->language][0]['value']   = $body_text;
                //$node->body[$node->language][0]['summary'] = text_summary($body_text);
                $node->body[$node->language][0]['format']  = 'full_html';
                $node->promote = 0; //nicht auf der Starseite
                
                // Alias erzeugen, post_name aus WP übernehmen
                $path = $akt_alias;
                $node->path = array('alias' => $path);
                
                /**Taxonomy Term zuordnen*/
                //id of your taxonomy term
                $tid = $akt_tag1;

                //add term to a node field
                //field_yourfield_name - machine name of your term reference field

                $node->field_tags[$node->language][0]['tid'] = $tid;
                /** end taxonomy term zuordnen */
                
                node_save($node);

Tags mitnehmen

Ich hab erstmal nur einen tag mitgenommen, aber das lässt sich leicht ausbauen. Sicherheitshalber frag ich auch noch ab, ob das tag-Feld nicht leer ist:

/**Taxonomy Terms zuordnen*/
                //id of your taxonomy term
                
                if ($akt_tag1 != ""){
                $tid1 = $akt_tag1;
                $node->field_tags[$node->language][0]['tid'] = $tid1;
                }
                
                if ($akt_tag2 !=""){
                $tid2 = $akt_tag2;
                $node->field_tags[$node->language][]['tid'] = $tid2;
                }
                
                if ($akt_tag3 !=""){
                $tid3 = $akt_tag3;
                $node->field_tags[$node->language][]['tid'] = $tid3;
                }
                
                //**end taxonomy terms zuordnen

Achtung: man muss in den Zeilen:

$node->field_tags[$node->language][]['tid'] = $tid3;

… den Parameter hinter $node->language leer lassen [], sonst werden die folgenden Tags nicht hinzugefügt, sondern überschrieben, so dass dann nur der letzte Tag zugeordnet ist.

Import komplett

Das wars jetzt aber – meine 24 Rezepte hat Drupal klaglos in den Blog importiert, die erscheinen auch brav im Block „Neueste Blogeinträge“:

neueste_blogbeiträge
neueste_blogbeiträge

Auch die Kategorien und die URL-aliase sind korrekt zugeordnet. Das nenn ich Spaß auf der Datenbank! 🙂

Jetzt lassen wir uns doch gleich mal das Inhaltsverzeichnis ausgeben:

ivz_24
ivz_24

Da hätte ich noch eine kleine Korrektur der Source für das Inhaltsverzeichnis nachzuliefern, das Array mit den Buchstaben braucht noch Anführungsstricherl, das sollte so aussehen, sonst wirft es PHP-Warnings:

$alfa = array('a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z');

Aber jetzt lass’mas gut sein, und nehmen eine Kaffeepause.

Drupal-Nodes programmatisch erzeugen

Node erstellen: ganz basic

Da ich ja nach wie vor auf über 300 Rezepten sitze, die ich nicht per Copy&Paste nach Drupal bringen will, hab ich mal nach Importmöglichkeiten gegooglet, und bin hier bei Group 42 fündig geworden. Der folgende Code erstellt einen Node vom Typ article, das funktioniert schon mal ganz gut. Was nicht so schön ist, ist dass die Sprache auf LANGUAGE_NONE gestellt wird, aber genau genommen stört das auch nicht weiter. Mal sehen, wie weit wir damit kommen. Hier der leicht modifizierte Code von Group 42:

/* Basic Node Creation Example for Drupal 7
        *
        * This example:
        * - Assumes a standard Drupal 7 installation
        * - Does not verify that the field values are correct
        */
        $body_text = '<h1>Text des neuen Nodes.</h1>';

        $node = new stdClass();
        $node->type = 'article';
        node_object_prepare($node);

        $node->title    = 'Node mit NID Evi full html';
        $node->language = LANGUAGE_NONE;

        $node->body[$node->language][0]['value']   = $body_text;
        //$node->body[$node->language][0]['summary'] = text_summary($body_text);
        $node->body[$node->language][0]['format']  = 'full_html';
        $node->promote = 0; //nicht auf der Starseite
        
        // Alias erzeugen, evtl. aus WP übernehmen
        //$path = 'content/programmatically_created_node_' . date('YmdHis');
        //$node->path = array('alias' => $path);

        node_save($node);

        echo "Node erzeugt, ID= ".$node->nid;

Besonders nützlich macht sich hier das:

node_object_prepare()

das nimmt einem schon einen Haufen Arbeit ab, indem es den neuen Node mit etlichen validen Werten vorbelegt, wer will kann das in der Drupal 7 API genauer nachlesen.

Eigentlich muss man jetzt nur noch den body text und den title mit eigenen Inhalten füttern, das könnte klappen. Das Format habe ich auf full_html gestellt, weil ich ja meine Überschriften-Headings aus WordPress übernehmen möchte.

Schön wärs natürlich, wenn man den alias auch gleich vernünftig anlegt, dafür könnte der post_name aus WordPress herhalten, aber das sehen wir dann schon noch, ob das was wird.

Kriegen wir die Kategorien als Tags hier auch mit rein?

Ja, kriegen wir! Und zwar mit folgender Ergänzung:

//id of your taxonomy term
        $tid = 8;

        //add term to a node field
        //field_yourfield_name - machine name of your term reference field

        $node->field_tags[$node->language][0]['tid'] = $tid;

Das macht man für mehrere Tags im Zweifelsfall einfach n mal. Damit sind wir bestens ausgerüstet und machen mal einen kurzen Break, weil ich erst in Access das Mapping von den WordPress-Kategorien auf die Drupal-Terms erstellen und mir eine vernünftige Export-Datei basteln muss.

WordPress-Kategorien als Drupal-Tags importieren

Bevor ich jetzt anfange, noch mehr Rezepte und Kategorien per Hand einzuhacken, schauen wir mal gleich, wie weit wir damit kommen diese aus WordPress zu importieren. Das wird eine ganz ähnliche Mechanik wie in Joomla werden, soviel kann ich jetzt schon absehen. Aber jetzt erstmal Schritt für Schritt. Und eine Warnung vorneweg: sowas macht man als Initial Load auf einer jungfräulichen Drupal-Instanz, wenn schon per Hand Kategorien als Tags angelegt wurden, hat man leicht doppelte Einträge und muss das dann erstmal bereinigen. Übrigens: kommen sie nicht auf die Idee, bereits vorhandene Tags einfach mit dem phpmyadmin aus der Datenbank zu löschen, das führt ganz schnell zu Inkonsistenzen!

Voraussetzungen

Wir ignorieren mal die Möglichkeit der geschachtelten Tags, die brauche ich nicht wirklich, eine Ebene reicht. Ich benutze auch das bereits vorhandene Vokabular Tags, das hat bei mir die vid=1. Die vorhandenen Taxonomien und vids sind  nachzuschauen in der Tabelle taxonomy_vocabulary, die zugehörigen Vocabulary-Einträge stecken in der taxonomy_term_data.

Ich brauche meine Kategorien zum Import in einer kleinen Tabelle, die hole ich mir aus Access und schubse sie als CSV nach MySQL rein.

Eine sehr praktische Funktion zum Import

Für den tatsächlichen Import benutze ich dann die Drupal-Funktion taxonomy_term_save($term). Die nimmt als Minimum-Parameter den Namen des Tags und die vid der Taxonomie, das sieht im ersten Testfall so aus:

    $name = 'Testwort2';
    $vid =1;
    $term = new stdClass();
    $term->name = $name;
    $term->vid = $vid;
    taxonomy_term_save($term);
    echo "Neuer Term erzeugt, ID = ".$term->tid;

Wichtig zu wissen: die Funktion erzeugt  immer einen neuen Eintrag in der taxonomy_term_data mit einer neuen tid, auch wenn der Name des Tags schon mal drinsteht. Ich gehe hier aber mal von einem Initial Load aus bzw. lösche vor dem Import die bereits vorhandenen Terms, die ich zum Testen manuell reingehackt habe, einfach raus.

Jetzt kommt unsere Import-Tabelle 00_kat_import ins Spiel, an der ist nicht viel dran, nur id und name, und die kennen wir auch schon:

kat_import_tabelle
kat_import_tabelle

Die lesen wir uns jetzt mit einem  db_query ein und speichern unsere neuen Terms in der Foreach-Schleife:

$query = db_query("SELECT name FROM 00_kat_import");
    $records = $query->fetchAll();
    
foreach ($records as $record) {
  
    // echo $record->name."<br>";
    $name = $record->name;
    $vid =1;
    $term = new stdClass();
    $term->name = $name;
    $term->vid = $vid;
    taxonomy_term_save($term);

  
} //end foreach

Und das war auch schon der ganze Zauber! Da sind die neuen Tags:

import_tags
import_tags

Diese Aktion macht man wie gesagt genau einmal als Initial Load. Wenn die Kategorien erstmal auf die Tags abgebildet sind, werden sie in Drupal weitergepflegt, sonst gibts allzuleicht Inkonsistenzen auf der Datenbank.

So, die Kategorien hätten wir schon mal. Jetzt bräuchten wir noch ein paar Rezepte…