Bilderkauderwelsch – Korrupte und fehlerhafte Galeriebilder nach einem WordPress Merge wiederherstellen

Als ich damals unsere Beiden WordPress-Blöge (oder Blogs, oder wie schreibt man das? *kopfkratz*) zusammengelegt hab, bin ich über den Standard Export und Import von WordPress gegangen. Hierzu werd ich mal einen gesonderten Artikel verfassen, allein schon deswegen, weil ich selbst dazu nichts fand im Netz. Wie dem auch sei… das klappte trotz der knapp 100GB an Daten und Bildern recht gut, wobei ich die Bilder im Hintergrund per FTP umgezogen habe. Obwohl zu Beginn auf dem ersten Blick alles gut aussah, waren beim zuletzt importieren Blog (der von Hoochi) die falschen Bilder zu sehen. Kommt Euch das also bekannt vor und sucht Ihr nach einer Lösung, so könnte Euch dieser Artikel hier – sprichwörtlich – den Arsch retten! Also verlieren wir keine Zeit und fangen an!

On Your Mark!

Bevor wir hier weiter einsteigen, gibt es einen kleinen Disclaimer, der aber eigentlich jedem klar sein sollte…

Wir hantieren hier am offenen Herzen, direkt im Hintergrund von WordPress und direkt auf mySQL-Ebene sowie mit einem direkten Inject über PHP-Snippets. Es gibt keine direkten Sicherheitsmechanismen bei allen folgenden Ausführungen, darum gilt auch hier: BACKUP, BACKUP, BACKUP!

Als langjähriger Entwickler rate ich zudem: Versucht zu verstehen was hier passiert und geht nicht stumpf ins “copy & paste” rein. Das hilft Euch in der Regel überhaupt gar nicht!

Ich werde hier gewisse Dinge nicht erläutern. Wenn Begriffe wie mySQL, Datenbanken, PHP und Codes euch Fremd sind, steigt aus. Lernt zunächst die Basics und kommt dann nochmal zurück. Klingt doof, isses aber nicht, sondern einfach nur ehrlich. Das hier ist kein “try & error” sondern ein “dead or alive” für Euren Blog.

Nachdem also alle Sympathien geklärt wären steigen wir ein.

Die Ausgangslage

Nach dem Merge sahen die Beiträge von Hoochi etwas komisch aus. Nein. Nicht der Text, der passte. Es war mehr der Bildliche Inhalt, allerdings nicht von einzelnen Bildern, sondern von den Standard WordPress-Galerien. Hier zwei Beispiele. Seht ihr die wunderschönen Kaminöfen?

Oder die harte Gartenarbeit?

Also ich weiß ja nicht. Aber wenn wir in Flugzeugaufnahmen Kaminöfen erkennen geht das meistens nicht gut aus für die Passagiere und ja… manchmal landet etwas vom Garten in den Tellern, allerdings nicht gerade irgendwelche umgemachten Bäume. Hier stimmte also etwas nicht…

Ich kontrollierte all Blogbeiträge und ausnahmslos alle waren Fehlerbehaftet. Allerdings nur bei Ihr.

Woran kann das liegen? Ich erkläre es Euch…

Die Ursache

Wenn ein Import durchgeführt wird, werden natürlich auch die Bilddaten migriert. Wir hatten den Vorteil, dass wir bei unseren Kameras eineindeutige Prefixe verwendet haben im Dateinamen. Schon immer. Dadurch konnten keine Bilder wirklich doppelt vorkommen. Nach dem Merge haben wir also z.B.

www.dasflosen.de/wp-content/pictures/2023/01/fa_bild1.jpg
www.hoochiswelt.de/wp-content/pictures/2023/01/ho_bild1.jpg

Das ist schon Mal nicht schlecht, weil nachdem in der Datenbank die URL zu www.schickischmi.de geändert wurde man immer noch die Eineindeutigkeit vorliegt

www.schickischmi.de/wp-content/pictures/2023/01/fa_bild1.jpg
www.schickischmi.de/wp-content/pictures/2023/01/ho_bild1.jpg

Das Problem sind allerdings nicht die Einzelbilder, sondern die WordPress-Galerien selbst. Diese enthalten nicht direkt die URL zum Bild, sondern nur die jeweilige Post-ID. Ich verdeutliche das gerade nochmal kurz und nehme nochmal das oben aufgeführte Beispiel, ergänze dies aber um die Post-ID als Prefix in [ ]. Aus den getrennten Bildern, die durchaus in den zuvor eigenständigen Blog-Seiten die selbe ID-Haben können…

[ID=1] www.dasflosen.de/wp-content/pictures/2023/01/fa_bild1.jpg
[ID=1] www.hoochiswelt.de/wp-content/pictures/2023/01/ho_bild1.jpg

…wird nach dem Import folgendes:

[ID=1] www.schickischmi.de/wp-content/pictures/2023/01/fa_bild1.jpg
[ID=2] www.schickischmi.de/wp-content/pictures/2023/01/ho_bild1.jpg

WordPress selbst erkennt das natürlich beim Import und passt die ID’s an, bzw. das macht die Datenbank im Hintergrund selbstständig, da die ID’s natürlich die Primärschlüssel darstellen. Und bevor hier fragen kommen. Ja, die Bildinformationen befinden sich ebenfalls in der “posts”-Tabelle der Datenbank. Dort, wo also auch die Beiträge etc. von WordPress gespeichert werden.

Ihr seid noch bei mir? Sehr gut, dann habt Ihr es quasi schon verstanden was hier passiert ist. Wenn man sich den Quelltext eines fehlerhaften Beitrags nun ansieht fällt auf, dass WordPress beim import – natürlich nicht – die ID’s in den Beiträgen anpasst. Das Bild das zuvor also die “ID1” trug und nach dem Import “ID2” wäre, ist im Content weiterhin “ID1” und somit ein anderes Bild als gewünscht.

Klingt kompliziert? Naja so schlimm isses gar nicht. Hier ein Auszug aus dem Artikel wenn wir mal in die Code-Ansicht gehen:

In diesem Screenshot seht Ihr die falsche Galerien mit den falschen Bildern. Die “ID10827” entspricht eben nicht mehr dem Bild, das es im vorherigen Blog besaß und genau das werden wir nun ändern…

Sry wen das jetzt sehr ausführlich gewesen war, aber ich will einfach, dass man versteht um was es hier geht und was wir hier tun werden.

Letzte Vorbereitungen

Bevor wir aber final loslegen, installieren wir noch kurz ein Plugin in WordPress. Da wir nicht alles auf Datenbankebene lösen können, ich kein (my)SQL-Gott bin, habe ich für die finale Aktion am Ende meine begnadeten PHP Skills ausgepackt und mir hierrüber eine kleine Hilfsfunktion gebaut und ausgeführt. Ich verwendete dieses Plugin:

Insert PHP Code Snippet – WordPress-Plugin | WordPress.org Deutsch

Es ist natürlich Euch überlassen ein anderes Snippet zu verwenden oder die Sites anders einzubauen. Evtl. könnt Ihr es auch direkt anders lösen. Für mich wars halt der schnellste Weg.

Zudem benötigt Ihr unbedingt die alten Datenbanken des vorherigen Blogs! Wenn Ihr diese nicht habt, braucht Ihr nicht mehr weiterlesen! Das ist leider Essenziell wichtig, weil wir nur so später die alten ID’s zu den Neuen zuordnen können. Oder eben umgekehrt.

Der Schlachtplan

Bevor wir einsteigen möchte ich noch kurz ein paar Begrifflichkeiten erklären, damit Ihr mir gedanklich folgen könnt.

Sourceblog = alter Blog von Hoochi
SourceDB = alte Datenbank vom alten Blog von Hoochi
Targetblog = neuer Blog -> Schickischmi
TargetDB = neue Datenbank des Merges
Tableprefix = es werden folgende Prefixe der WordPress-Tabellen verwendet: hw_ = hoochiswelt, puew_ = schickischmi

Kommen wir also nun zum pratikschen Teil. Theorie hatten wir genug. Hier nochmal unser Fahrplan was Euch erwarten wird:

SourceDB:

  1. Alle ID’s aus allen veröffentlichten Blogpost innerhalb des Gallery-Tags im Content extrahieren und in eine gesonderte Tabelle schreiben
  2. Die Tabelle entsprechend bereinigen und die ID-Strings in einzelne Zeilen aufteilen
  3. Tabelle exportieren

TargetDB:

  1. Tabelle in den neuen Blog importieren und import um 2 Spalten erweitern
  2. Gleiche Tabelle wie in der SourceDB in der TargetDB zum Abgleich erstellen (Punkt 1&2 aus der SourceDB werden dort also nochmal wiederholt)
  3. Merge der Tabelleninformationen
  4. Bereinigung/Ergänzung der Tabelle
  5. PHP Snippet erstellen
  6. PHP Snippet ausführen, davor noch kurz zur Beichte gehen und ID’s in den Post-Contents automatisiert ändern lassen
  7. Freude oder Weinen

SourceDB – Extrahieren der IDs aus den Galerie-Tags

Gehen wirs an. Wir öffnen unseren mySQL-Editor unserer Wahl und verbinden uns mit der SourceDB-mySQL. Wir wollen jetzt aus den “hw_posts” alle IDs extrahieren und in eine Zwischentabelle speichern. Der Inhalt der Blogposts ist in der Spalte “contents”.

Die Zwischentabelle nennen wir “gallery_id”. Hierzu führen wir im Editor unseres mySQL-Studios folgendes Statement aus:

CREATE TABLE
    gallery_id AS
    SELECT DISTINCT
        p.ID AS post_id,
REPLACE(REPLACE(SUBSTRING_INDEX(SUBSTRING_INDEX(SUBSTRING_INDEX(p.post_content,'', -1),'ids="', -1), '"', ''), '</p>', '') AS gallery_ids
    FROM
        puew_posts p
    JOIN (
        SELECT
            a.N + b.N * 10 + 1 AS n
        FROM 
            (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a
        CROSS JOIN 
            (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b
    ) AS numbers
    WHERE
        p.post_type = 'post'
        AND p.post_status = 'publish'
        AND p.post_content LIKE '%'
    ORDER BY
        post_id, gallery_ids;
CREATE TABLE puew_unique_gallery_ids_with_guid (new_post_id BIGINT UNSIGNED,     new_gallery_id INT,new_guid VARCHAR(255) );
INSERT INTO puew_unique_gallery_ids_with_guid (new_post_id, new_gallery_id, new_guid)
SELECT u.post_id, u.gallery_id, p.guid
FROM (
    SELECT post_id,
           CAST(SUBSTRING_INDEX(SUBSTRING_INDEX(gallery_ids, ',', numbers.n), ',', -1) AS UNSIGNED) AS gallery_id
    FROM new_gallery_id
    JOIN (
        SELECT a.N + b.N * 10 + 1 AS n
        FROM 
        (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS a
        CROSS JOIN 
        (SELECT 0 AS N UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) AS b
    ) AS numbers
    ON CHAR_LENGTH(gallery_ids) - CHAR_LENGTH(REPLACE(gallery_ids, ',', '')) >= numbers.n - 1
) AS u
JOIN puew_posts p ON u.gallery_id = p.ID;

Das reicht, mehr müssen wir damit nicht tun, wir kümmern uns weiter um die von uns importierte und bereits angereicherte Tabelle aus der SourceDB.

TargetDB – Abgleich der Beiden Unique_Tabellen

Jetzt holen wir uns somit die richtige BeitragsID (PostID) in unsere importierte Tabelle aus der SourceDB. Als Schlüssel dient uns hier die GUID. Ihr erinnert Euch, wir haben die GUID geändert und der Pfad aus der GUID ist nun eineindeutig zuweißbar und perfekt für den Abgleich.

Kurz zur Veranschaulichung:

[Source GUID] www.schickischmi.de/wp-content/pictures/2023/01/ho_bild1.jpg
[Target GUID] www.schickischmi.de/wp-content/pictures/2023/01/ho_bild1.jpg

Somit holen wir uns jetzt die letzte Information über folgendes Statement:

UPDATE hw_unique_gallery_ids_with_guid t
JOIN puew_unique_gallery_ids_with_guid p ON t.old_gallery_id = p.old_gallery_id
SET t.new_post_id = p.old_post_id;

Jetzt sieht das doch schon mal nach was aus! Wir haben’s fast geschafft! Dranbleiben jetzt!

Der Beitrag “10799” aus der SourceDB entspricht in der TargetDB dem Beitrag “15400”. Die alte BildID “15054” aus dem alten Beitrag, entspricht im neuen Beitrag der BildID “15055”. Was wir aber noch sehen, sind u.U. ein paar “0”er-Einträge.

Dort hat die Zuordnung nicht ganz geklappt. Kein Problem, da wir ja gültige Zuordnungen haben und wissen dass “10799” der “15400” entspricht. Somit leiten wir daraus kurz ein Letztes und finales Statement ab:

UPDATE hw_unique_gallery_ids_with_guid AS t1
JOIN (
    SELECT old_post_id, MAX(new_post_id) AS max_new_post_id
    FROM hw_unique_gallery_ids_with_guid
    WHERE new_post_id != 0
    GROUP BY old_post_id
) AS t2 ON t1.old_post_id = t2.old_post_id
SET t1.new_post_id = t2.max_new_post_id
WHERE t1.new_post_id = 0;

Ihr könnt das Ganze im Übrigen auch nochmal Kontrollieren ob die Bilder tatsächlich auch da sind. Haben wir das aber eigentlich nicht erst? Naja, wir hätten uns da quasi selbst beschissen, denn natürlich kann das Bild über die URI aufrufbar sein wenn es im Hintergrund auf dem Server liegt, allerdings muss es auch in der WordPress Mediathek vorhanden sein! Und somit suchen wir kurz das Bild in der Mediathek. Ich würde hier ein paar Stichproben empfehlen! Betrachten wir also unser finales Werk. Wow!

Hach… eine Prachttabelle. Jetzt gehen wir rüber ins ins PHP-Snippet.

Die Finalisierung über das Snippet

Leider konnte ich die folgende Aktionen im mySQL nicht ausführen, weshalb ich mir hier WordPress und PHP zu Nutzen machte. Bisschen Coding schadet nie und es war für mich weniger aufwendiger.

Unser erstellte Tabelle wollen wir jetzt Zeile für Zeile durchgehen. Wir suchen dann in der entsprechenden “new_post_id” in der Spalte “post_content” nach der ID und ersetzen diese durch die “new_gallery_id”. Das Ganze wollen wir noch schön Protokollieren für uns damit wir auch was sehen. Wir estellen also folgenden Code:

<?php    
global $wpdb;

// Select rows from your_table_name
$query = "SELECT new_post_id, old_gallery_id, new_gallery_id FROM hw_unique_gallery_ids_with_guid WHERE old_gallery_id != new_gallery_id AND new_post_id != 0 AND new_gallery_id != 0" ;

$results = $wpdb->get_results($query);
$counts = 0;

foreach($results as $rowdata){
    $postId = $rowdata->new_post_id;
    $oldGalleryId = $rowdata->old_gallery_id;
    $newGalleryId = $rowdata->new_gallery_id;
    echo "===========================<br>Starting New Row: ";
    echo "<br>";
    echo "Replacing...";
    echo "<br>";
    echo "Old Gallery ID: ";
    echo $oldGalleryId;
    echo "<br>";
    echo "New Gallery ID: ";
    echo $newGalleryId;
    echo "<br>";
    echo "In Post: ";
    echo $postId;
    echo "<br>";
    echo "Replace Proccess<br>";
    $updateQuery = "UPDATE puew_posts SET post_content = REPLACE(post_content, '$oldGalleryId', '$newGalleryId') WHERE ID = '$postId'";
    $wpdb->query($updateQuery);
    echo "Process done<br>===========================<br>";
    $counts++;
}
    echo "<br>===========================<br>";
    echo "<br>===========================<br>";

echo "Main Rows done:";
echo $counts;

?>

Am Besten ihr versucht es zunächst mal nur an einem Beitragsblog. Ich nahm den bereits erwähnten “15400” her, welcher auch dem Eingangsbeispiel entsprach. Wichtig, es geht JETZT um die ID des Beitrags aus dem TargetBlog. Für den Test passen wir im oben aufgeführten PHP das Script noch kurz das “SELECT-Statement” an

$query = "SELECT new_post_id, old_gallery_id, new_gallery_id FROM hw_unique_gallery_ids_with_guid WHERE old_gallery_id != new_gallery_id AND new_post_id != 0 AND new_gallery_id != 0 AND new_post_id = 15400"

Jetzt speichern und ausführen. Und es sollte sich was tun!

Sieht erstmal gut aus. Wir gehen tiefer rein und checken gegen. Ihr merkt schon… Kontrolle ist immer gut und ich mag Kontrolle *lach*. Im Code-Editor des Beitrages suchen wir nach der alten ID

Fehlanzeige… keine Treffer. Gutes Zeichen! Wir suchen nach der neuen ID…

…BINGO! Wir öffnen den Artikel und siehe da… es sieht bisschen anders aus. Kaminöfen sind wieder Kaminöfen.

Und auch aus Foodporn wurde jetzt Garten

Und jetzt? Setzen wir den Filter im Snippet zurück und lassen alles durchlaufen. Das dauert jetzt u.U. etwas länger… Bei mir waren es über 2208 Bildern in den Galerien.

Und jetzt?

Freude! Wenn Ihr in Euren Posts jetzt auch wieder die richtigen Bilder seht, habt Ihr alles Richtig gemacht und ich es wohl ganz gut erklärt. Am Ende sei noch kurz gesagt:

Wenn Ihr noch Verbesserungsvorschläge habt, gerne her damit. Wahrscheinlich geht’s auch mit nem 5-Zeiler am Ende des Tages *lach* Und ja, ich mach noch nen Artikel zum allgemeinen Merge. Bis dahin… haut rein!

Interessiert Dich auch?

Kommentar verfassen