Migrating from WordPress to CakePHP
Posted on 24/9/07 by Felix Geisendörfer
Hey folks,
I'm currently working on migrating my blog from WordPress to a light-weight CakePHP replacement that I hope to enhance and customize to my liking easily in future. Its not like WordPress has treated me badly, if it wasn't around I'd probably never have started this blog and never gotten to where I am right now. While I'm giving out kudos, I'd also like to mention that you, the readers of this blog have been an amazingly good audience by sharing your knowledge and opinions on PHP programming with me, while also being very encouraging about the whole thing. I really feel bad when I don't get to blog, so time to do it again : ).
In this post I'm simply going to throw out some snippets to show how I'm currently approaching the whole process in order to give both my insight into what works and what doesn't, while also hoping to get some people to share their insight into migrating legacy apps to CakePHP.
So one of the first things I started was to do a new layout and come up with the visual screen elements that I want to integrate. The next thing I did (and still am doing) is to start a little migration script using the new Cake 1.2 console to get my data moved in the new db schema. I haven't finalized the new schema yet and kind of make it up as I go, run a migration and then implement the functionality to show the data currently available. So far I've been working on migrating the wp_posts and wp_comments table. The first thing that hit me was that WordPress is not good about conventions. For example the primary key on my wp_posts table is 'ID' while it is 'comment_ID' on the comments table. I soon realized that one of the biggest tasks would be to map old Wp table fields to ones that have different names in my new CakePHP blog. But before I go in too much detail, I'll paste the migration script I currently have and then explain it a little further:
uses('model'.DS.'model');
class WpMigrationShell extends Shell {
var $uses = array(
'WpPost'
, 'WpComment'
);
var $times = array();
function initialize() {
$this->times['start'] = microtime(true);
parent::initialize();
}
function main() {
$this->out('Migrating WordPress posts ...');
$this->WpPost->migrate();
$this->out('Migrating WordPress comments ...');
$this->WpComment->migrate();
$this->out('Populating WordPress posts counter cache ...');
$this->WpPost->populateCounterCache();
$this->times['end'] = microtime(true);
$duration = round($this->times['end'] - $this->times['start'], 2);
$this->hr();
$this->out('Migration took '.$duration.' seconds.');
}
}
class WpMigrationModel extends Model{
var $useDbConfig = 'wordpress';
var $newModel = null;
var $map = array();
function migrate() {
if (empty($this->newModel)) {
$this->newModel = substr($this->name, 2);
}
loadModel($this->newModel);
$Model = new $this->newModel();
$Model->execute('TRUNCATE '.$Model->table);
$methods = get_class_methods($this);
$keys = array_keys($this->map);
$idKey = $keys[0];
$oldEntries = $this->findAll(null, $keys);
foreach ($oldEntries as $oldEntry) {
if (!$this->filter($oldEntry[$this->name])) {
continue;
}
$id = $oldEntry[$this->name][$idKey];
$Model->create();
foreach ($this->map as $oldField => $newField) {
$value = $oldEntry[$this->name][$oldField];
$migrateFct = 'migrate'.Inflector::camelize($newField);
if (in_array($migrateFct, $methods)) {
$value = $this->{$migrateFct}($value);
}
$Model->set($newField, $value);
}
if (!$Model->save()) {
die('Could not save '.$this->newModel.' #'.$id);
}
}
}
function filter() {
return true;
}
function migrateText($text) {
return utf8_decode($text);
}
}
class WpPost extends WpMigrationModel{
var $useTable = 'posts';
var $map = array(
'ID' => 'id'
, 'post_title' => 'title'
, 'post_content' => 'text'
, 'post_status' => 'published'
);
function populateCounterCache() {
$Post = new Post();
$Post->recursive = -1;
$Post->Comment->recursive = -1;
$posts = $Post->findAll(array('id'));
foreach ($posts as $post) {
$Post->set($post);
$comment_count = $Post->Comment->findCount(array('post_id' => $Post->id));
if ($comment_count) {
$Post->set(compact('comment_count'));
$Post->save();
}
}
}
function filter($item) {
if (!in_array($item['post_status'], array('publish', 'draft', 'private'))) {
return false;
}
return true;
}
function migratePublished($value) {
if ($value == 'publish') {
return true;
}
return false;
}
}
class WpComment extends WpMigrationModel{
var $useTable = 'comments';
var $map = array(
'comment_ID' => 'id'
, 'comment_post_ID' => 'post_id'
, 'comment_author' => 'author_name'
, 'comment_author_email' => 'author_email'
, 'comment_author_url' => 'author_url'
, 'comment_content' => 'text'
);
function filter($item) {
static $Post;
if (empty($Post)) {
$Post = new Post();
}
$Post->set('id', $item['comment_post_ID']);
return $Post->exists();
}
function migrateAuthorName($name){
return utf8_decode($name);
}
}
Alright there is quite some stuff going on here so let me begin with the basics. I've decided to give all WordPress tables their own model which I prefix with 'Wp' to not overlap with the models I'm using in the new CakePHP application. Those 'Wp' models all extend a common base model which I call the WpMigrationModel. This is because I found that a lot of functionality is shared across them, especially things like mapping old 'Wp' fields to new fields for my CakePHP models as well as filtering out items I'm not interested anymore. All of this is happening in the WpMigrationModel::migrate. Another neat thing I built in is that while the algorithm loops through the fields of my new cake models, it also looks for a function
Anyway, this of course is just the beginning, but its already most of the data I'm really interested in migrating - I don't mind loosing a couple meta / whatever fields. In a next post I'll show how to replicate some legacy WordPress logic in my new cake blog. For now I've got to stop as my plane to San Francisco from the Atlanta airport is boarding ; ).
-- Felix Geisendörfer aka the_undefined
PS: Typos may be corrected when I get wifi again ; ).
You can skip to the end and add a comment.
Hey, I've been thinking on doing this for a while.
The thing is that wordpress is a great software (and now with svn updates much more) and the little time I have free have kept me from trying anything. I don't know if you intend to have a plugin interface like the one wordpress has, it would be nice.
Jonathan Snook's blog runs on cakephp too, so you're not the only one.
I'm really interested to see what you come up with, and also to help if you need any.
Good Luck.
Eventhough it's in Dutch and not everything is working yet (hence the strikethroughs here and there), my blog runs on cakePHP too.
He-he =)
I'm allready there. And extremely happy about it.
Now I'm working on trackbacks and XML-RPC =)
Joaquin: What I'll do is to set up the new blog on my dev server pretty soon and invite everybody who is interested in checking it out and trying it out. The data on there will be migrated from the "real" blog every hour or so using a cron job so the comments made on the new one will not be permanent but good enough for testing purposes.
-- Felix
Another approach, which I did for a client recently, is to integrate WordPress into CakePHP. I wish I could publish the code, but it wasn't all that hard.
Basically I dropped an installation of WordPress into Cake's webroot and did a normal install. Then I altered wp-config.php to pull in database settings from Cake's config/database.php. Then I made a customized wordpress template that pulls in a cake layout, essentially rendering the blog as the content_for_layout.
All the WordPress functionality (posts, trackbacks, pings, comments, pluggins...) work as normal. The only negative is having a separate admin area.
Matt: No I want to see no WordPress around anymore. I had a good time with it and this blog wouldn't exist if it wasn't for WordPress, but its time to move on ; ).
Why not code igniter ? ;)
@sf devblog: Try CakePHP if you want to find out ; ).
Hey Felix--I am doing something similar at It's what I am starting to use instead of Wordpress for my personal and client work.
Hello!
Very Interesting post! Thank you for such interesting resource!
PS: Sorry for my bad english, I'v just started to learn this language ;)
See you!
Your, Raiul Baztepo
This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.
[...] Felix Geisendorfer is in the process of moving his blog from Wordpress to a customized “light-weight CakePHP replacement” he’s developing to be extended later on. His post shares some of the tips he found so far. In this post I’m simply going to throw out some snippets to show how I’m currently approaching the whole process in order to give both my insight into what works and what doesn’t, while also hoping to get some people to share their insight into migrating legacy apps to CakePHP. [...]