Import postów i zdjęć do WordPressa

W tym wpisie przedstawię przykładowe rozwiązanie umożliwiające import postów i zdjęć z bazy danych do WordPressa. Pewnie istnieją wtyczki które to ułatwiają, jednak tu przedstawię jak to zrobić w PHP i MySQL. Ten wpis powstał na podstawie doświadczenia – niedawno realizowałem taki import kilkudziesięciu tysięcy postów ze starej bazy danych jakiegoś dedykowanego CMS-a właśnie do WordPressa.

Schemat bazy danych

Dla ułatwienia załóżmy że wordpress jest zainstalowany na tej samej bazie danych MySQL co baza z której chcemy importować wpisy. Załóżmy że tak wygląda schemat starej bazy:

CREATE TABLE IF NOT EXISTS articles (
  id int(11) NOT NULL AUTO_INCREMENT,
  title varchar(128) NOT NULL,
  content text NOT NULL,
  short text NOT NULL,
  category_id int(11) DEFAULT NULL,
  date datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS categories (
  id int(11) NOT NULL AUTO_INCREMENT,
  name varchar(128) NOT NULL,
  PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

CREATE TABLE IF NOT EXISTS photos (
  id int(11) NOT NULL AUTO_INCREMENT,
  url varchar(128) NOT NULL,
  article_id int(11) NOT NULL,
  date datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (id)
) ENGINE=MyISAM DEFAULT CHARSET=latin1;

Widzimy tutaj 3 tabele: jedna z artykułami, druga z kategoriami a trzecia ze zdjęciami. Załóżmy że dla każdego wpisu pobierzemy jedno zdjęcie i wykorzystamy je jako miniaturkę.

Skrypt importu

Zacznijmy pisanie naszego skryptu. Zaczynamy od łączenia z bazą danych:

<?php
try{
  $db = new PDO('mysql:host=localhost;dbname=wordpress', 'root', 'haslodobazy');
}
catch (PDOException $e){
  die ("Error connecting to database!");
}

WordPress wymaga aby wszystkie wpisy miały tzw. „slug”. W starej bazie go nie ma, więc dodajmy przykładową funkcję PHP do jej generowania:

function slug(string $string){
  $string = trim(str_replace([' ','&','%','$',':',',','/','=','?','Ę','Ó','Ą','Ś','Ł','Ż','Ź','Ć','Ń','ę','ó','ą','ś','ł','ż','ź','ć','ń'], ['-','-','-','','','','','','','E','O','A','S','L','Z','Z','C','N','e','o','a','s','l','z','z','c','n'], $string));
  $string = preg_replace("/[^a-zA-Z0-9-_]+/", "", $string);
  $string = trim($string,'-');
  do{
    $string_old = $string;
    $string = str_replace("--", "-", $string);
}while($string != $string_old);
  return strtolower($string);
}

Zacznijmy od wstawienia kategorii. Zróbmy zapytanie które pobierze kategorie ze starej bazy i wstawi je do nowej. W przypadku gdy w starej mamy już kategorie i nie chcielibyśmy ich usuwać a jest ryzyko że ID kategorii się powtórzy z nowej i starej bazy, możemy przy imporcie dodawać stałą liczbę, większa od maksymalnego ID kategorii z obecnej bazy WordPressa. Czyli jeśli ostatnie ID kategorii z WordPressa wynosi 97 moglibyśmy dodawać np 100 do każdej przy imporcie.

define('ADD_TO_ID_CATEGORY', 10); // możemy dodawać inną stałą do ID kategorii
define('ADD_TO_ID_POST', 10); // możemy dodawać inną stałą do ID wpisu
$sth = $db->query('SELECT * FROM categories');
$sth2 = $db->prepare('INSERT INTO wp_terms(term_id, name, slug) VALUES (:term_id,:name,:slug)');
$sth3 = $db->prepare('INSERT INTO wp_term_taxonomy(term_taxonomy_id, term_id, taxonomy, description, parent, count) VALUES (:term_taxonomy_id,:term_id,"category","",0,0)');
foreach($sth as $row){
  $sth2->bindValue(':term_id', ($row['id']+ADD_TO_ID_CATEGORY), PDO::PARAM_INT);
  $sth2->bindValue(':name', $row['name'], PDO::PARAM_STR);
  $sth2->bindValue(':slug', slug($row['name']), PDO::PARAM_STR);
  $sth2->execute();
  $sth3->bindValue(':term_taxonomy_id', ($row['id']+_ADD_TO_ID_CATEGORY_), PDO::PARAM_INT); $sth3->bindValue(':term_id', ($row['id']+_ADD_TO_ID_CATEGORY_), PDO::PARAM_INT); $sth3->execute();
}

Sam kod zapisujemy w pliku php, wrzucamy do katalogu głównego WordPressa i następnie go uruchamiamy z przeglądarki lub z konsoli.

Teraz zróbmy zapytanie do wstawiania nowych rekordów z wpisami do bazy danych wordpressa (do tabeli wp_posts) i jednocześnie pętle która pobierze rekordy ze starej bazy i je wstawi do WordPressa:

$sth = $db->query('SELECT * FROM articles');
$sth2 = $db->prepare('INSERT INTO wp_posts(ID, post_author, post_date, post_date_gmt, post_content, post_title, post_excerpt, post_status, comment_status, ping_status, post_password, post_name, to_ping, pinged, post_modified, post_modified_gmt, post_content_filtered, post_parent, guid, menu_order, post_type, post_mime_type, comment_count) VALUES (:ID,1,:post_date,:post_date,:post_content,:post_title,:post_excerpt,"publish","open","open","",:post_name,"","",:post_date,:post_date,"",0,"",0,"post","",0)');
foreach($sth as $row){
  $sth2->bindValue(':ID', ($row['id']+ADD_TO_ID_POST), PDO::PARAM_INT);
  $sth2->bindValue(':post_date', date('Y-m-d H:i:s',strtotime($row['date'])), PDO::PARAM_STR);
  $sth2->bindValue(':post_content', $row['content'], PDO::PARAM_STR);
  $sth2->bindValue(':post_title', $row['title'], PDO::PARAM_STR);
  $sth2->bindValue(':post_name', slug($row['title']), PDO::PARAM_STR);
  $sth2->bindValue(':post_excerpt', $row['short'], PDO::PARAM_STR);
 $sth2->execute();
}

Skrypt ten zakłada że baza postów WordPressa jest pusta (ale raczej będzies miał przynajmniej jedną stronę). Jeśli tak nie jest to możemy do ID każdego postu dodawać stałą liczbę, większą od ID ostatniego postu z WordPressa. Czyli jeśli np ID ostatniego postu to 96, możemy do każdego importowanego dodawać 100, dzięki czemu nie będziemy mieli problemu z powtarzającymi się ID postów. Jak możemy zobaczyć datę wstawiamy w formacie „Y-m-d H:i:s” a w pole „post_name” wstawiamy slug tytułu.

Teraz przypiszmy posty do kategorii:

$sth = $db->query('SELECT * FROM articles');
$sth2 = $db->prepare('INSERT INTO wp_term_relationships(object_id, term_taxonomy_id) VALUES (:object_id,:term_taxonomy_id)');
foreach($sth as $row){
  $sth2->bindValue(':object_id', ($row['id']+ADD_TO_ID_POST), PDO::PARAM_INT);
  $sth2->bindValue(':term_taxonomy_id', ($row['category_id']+ADD_TO_ID_CATEGORY), PDO::PARAM_INT);
  $sth2->execute();
}

Teraz przyszedł czas na import miniaturek. Załóżmy, że chcemy z tabeli photos zaimportować po jednym zdjęciu które będzie miniaturką wpisu i od razu przenieść je do właściwego katalogu. Załóżmy że zdjęcia są w katalogu /zdjecia i że w starej bazie danych jest data zapisu zdjęcia do bazy którą wykorzystamy. Jeśliby jej nie było to moglibyśmy np wszystkie zdjęcia przerzucić do katalogu /wp-content/uploads/2020/11/ i wtedy pod zmienną folder dajemy '2020/11/’.

WordPress zapisuje zdjęcia (media) w tej samej tabeli co wpisy.

define('FOLDER_PHOTOS_OLD', 'zdjecia/');
define('FOLDER_PHOTOS', 'wp-content/uploads/');

$sth = $db->query('SELECT * FROM photos GROUP BY article_id');

$sth2 = $db->prepare('INSERT INTO wp_posts(post_author, post_date, post_date_gmt, post_content, post_title, post_excerpt, post_status, comment_status, ping_status, post_password, post_name, to_ping, pinged, post_modified, post_modified_gmt, post_content_filtered, post_parent, guid, menu_order, post_type, post_mime_type, comment_count) VALUES (1,:post_date,:post_date,"",:post_title,"","inherit","open","closed","",:post_title,"","",:post_date,:post_date,"",:post_parent,:guid,0,"attachment","image/jpeg",0)');

$sth3 = $db->prepare('INSERT INTO wp_postmeta(post_id, meta_key, meta_value) VALUES (:post_id,"_thumbnail_id",:meta_value)');

$sth4 = $db->prepare('INSERT INTO wp_postmeta(post_id, meta_key, meta_value) VALUES (:post_id,"_wp_attached_file",:meta_value)');

$sth5 = $db->prepare('INSERT INTO wp_postmeta(post_id, meta_key, meta_value
) VALUES (:post_id,"_wp_attachment_metadata",:meta_value)');

foreach($sth as $row){
  $path_parts = pathinfo(_FOLDER_PHOTOS_OLD_.$row['url']); $size = getimagesize(_FOLDER_PHOTOS_OLD_.$row['url']); $data = [ 'width' => $size[0], 'height' => $size[1], 'file' => $path_parts['basename'];
  if(!file_exists(_FOLDER_PHOTOS_.date('Y',strtotime($row['date'])))){ 
    mkdir(_FOLDER_PHOTOS_.date('Y',strtotime($row['date']))); }         $folder = date('Y',strtotime($row['date'])).'/'.date('m',strtotime($row['date'])).'/'; 
if(!file_exists(_FOLDER_PHOTOS_.$folder)){ mkdir(_FOLDER_PHOTOS_.$folder); } copy (_FOLDER_PHOTOS_OLD_.$row['url'], _FOLDER_PHOTOS_.$folder.$path_parts['basename'] ); 
$sth2->bindValue(':post_date', date('Y-m-d H:i:s',strtotime($row['date'])), PDO::PARAM_STR);
$sth2->bindValue(':post_title', $path_parts['filename'], PDO::PARAM_STR); 
$sth2->bindValue(':post_parent', ($row['article_id']+_ADD_TO_ID_POST_), PDO::PARAM_INT); $sth2->bindValue(':guid', 'http://example.com/wp-content/uploads/'.$folder.$path_parts['basename'], PDO::PARAM_STR); 
$sth2->execute(); 

$id = $db->lastInsertId();

$sth3->bindValue(':post_id', ($row['article_id']+_ADD_TO_ID_POST_), PDO::PARAM_INT); 
$sth3->bindValue(':meta_value', $id, PDO::PARAM_STR);
$sth3->execute(); 

$sth4->bindValue(':post_id', $id, PDO::PARAM_INT); 
$sth4->bindValue(':meta_value', $folder.$path_parts['basename'], PDO::PARAM_STR); 
$sth4->execute(); 

$sth5->bindValue(':post_id', $id, PDO::PARAM_INT); 
$sth5->bindValue(':meta_value', serialize($data), PDO::PARAM_STR); 
$sth5->execute();
}

Jak widać trzeba pobrać wymiary obrazków. Pod example.com wstawiamy adres www naszej strony.

Bonus: A co zrobić jeśli w starej bazie w samych artykułach były dodane obrazki? Załóżmy, że obrazki mają parametr src zaczynający się od „zdjecia/”.
Możemy wykorzystać poniższy kod:

$sth2 = $db->prepare('UPDATE wp_posts SET post_content=:post_content WHERE ID=:ID limit 1');

$sth3 = $db->prepare('INSERT INTO wp_posts(post_author, post_date, post_date_gmt, post_content, post_title, post_excerpt, post_status, comment_status, ping_status, post_password, post_name, to_ping, pinged, post_modified, post_modified_gmt, post_content_filtered, post_parent, guid, menu_order, post_type, post_mime_type, comment_count) VALUES (1,:post_date,:post_date,"",:post_title,"","inherit","open","closed","",:post_title,"","",:post_date,:post_date,"",:post_parent,:guid,0,"attachment","image/jpeg",0)');

$sth = $db->query('SELECT * FROM wp_posts WHERE post_content LIKE \'%<img%src="zdjecia%\'');
foreach($sth as $row){

  $post_content = $row['post_content'];

  $doc = new DOMDocument();
  libxml_use_internal_errors(true);
  $doc->loadHTML( $row['post_content'] );
  $xpath = new DOMXPath($doc);
  $imgs = $xpath->query("//img");
  for ($i=0; $i < $imgs->length; $i++) {
    $img = $imgs->item($i);
    $src = $img->getAttribute("src");

    if(substr($src, 0, 8)=='zdjecia/'){

        if(file_exists($src)){
            $path_parts = pathinfo($src);

            copy ($src, _FOLDER_PHOTOS_.$path_parts['basename'] );

            $post_content = str_replace($src,"/wp-content/uploads/2020/11/".$path_parts['basename'],$post_content);

            $sth2->bindValue(':ID', $row['ID'], PDO::PARAM_INT);
            $sth2->bindValue(':post_content', $post_content, PDO::PARAM_STR);
            $sth2->execute();

            $sth3->bindValue(':post_date', date('Y-m-d H:i:s'), PDO::PARAM_STR);
            $sth3->bindValue(':post_title', $path_parts['filename'], PDO::PARAM_STR);
            $sth3->bindValue(':post_parent', $row['ID'], PDO::PARAM_INT);
            $sth3->bindValue(':guid', 'http://example.com/wp-content/uploads/2020/11/'.$path_parts['basename'], PDO::PARAM_STR);
            $sth3->execute();

        }else{

            $new_src = str_replace('src="uploads/','src="/wp-content/uploads/2020/11/',$src);

            if(file_exists(realpath(dirname(__FILE__)).$new_src)){

                $post_content = str_replace($src,$new_src,$post_content);

                $sth2->bindValue(':ID', $row['ID'], PDO::PARAM_INT);
                $sth2->bindValue(':post_content', $post_content, PDO::PARAM_STR);
                $sth2->execute();
            }
        }
    }
  }
}

Na koniec trzeba będzie wygenerować miniaturki w pozostałych wymiarach. W tym celu możemy użyć gotowej wtyczki np. https://pl.wordpress.org/plugins/regenerate-thumbnails/

Podsumowanie

Cały kod PHP przykładu możesz znaleźć na GitHubie: https://github.com/kamilwyremski/import-postow-i-zdjec-do-wordpressa

Mam nadzieje, że ten wpis pomoże Ci w imporcie starej bazy danych do WordPressa. Oczywiście sam skrypt nie jest idealny, jednak celem wpisu było przedstawienie zarysu jak można to zrobić. Jest on przeznaczony dla programistów mających pojęcie o PHP i MySQL. W komentarzach możesz się podzielić czy udało Ci się go wykorzystać albo co sugerujesz poprawić.

Ta strona używa ciasteczek (cookies), dzięki którym nasz serwis może działać lepiej. Więcej informacji

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close