Migrating a PHP database to a new Rails application

After rewriting most of a PHP application in Ruby on Rails, I realized I had a problem with the database.  How to I get all the data from the database and put it into my rails database?  The easy answer would probably be to copy it all into the /db/seeds.rb file and just migrate.  But that sounds like a lot of typing!  And typing means more mistakes.  Also, how would I do this in the future?  The app wasn’t that big, but it was big enough that a little strategy was called for.

I started following this post.  This told me that I really needed to create a plugin to do it.  Looking a bit further, I found Tim Riley’s acts-as-importable. That looked very promising and was what I eventually used.  This app is on Rails 2.5.  Here’s what I did:

Step 1 – Get the code!

First up, I got the data.

After that I didn’t know where to put it.  Turns out, you just expand it so that you have a directory called /vendor/plugins/acts-as-importable.

From there, you should have a directory structure that looks like this:

While we’re here, I don’t remember if I modified the init.rb, but mine looks like this:

Step 2 add database config and back it up!

Then I figured I’d add the database of my legacy system to the config/database.yml file:

Since this database was active and on the other server, I backed it up just in case!

Then I took some time to make sure I could indeed access it with that username and password remotely, since I develop remotely.  This took some fun mysql-foo.  It turned out that my Mac didn’t have any mysql client on there.  Not a problem:

Next up, we had to create some classes.

step 3: Create classes

This was well documented on Tim’s blog.  What wasn’t obvious to me was where to put the classes.  Luckily, in the comments section there was somebody who asked the question.  Thanks for asking  questions!  They really help!

So I created app/models/legacy.rb Here it is in all its glory.  I broke up the fille into 3 sections, but its all just one file.

Now for some explanations.  This is a really simple app.  Its just a cooking web site with users, recipes, and categories.  Nothing more fancy than that.   Starting with the Category class, we imported all the old ‘cat’ columns into the new Category.title attribute.  In addition, we did a find_by_title to make sure it wasn’t already in the application.  This made it so we didn’t have duplicate entries and I could run this as much as I want while letting the legacy application stay in place until I was finished developing.

Next up the user stuff.  This was pretty much the same as the Category, but added a few more things.  I lost the users passwords this way because I set up a new password system with bcrypt that was more secure.  It was ok, I notified all the users later by generating default passwords.

The last model class is the recipe.  This was a bit more tricky because of errors in the way the first database was created.  Notice we had to use the belongs_to in this class and the has_many in the previous classes.  One of the issues I had was that in the legacy database, there was no foreign key from the Recipes to the Users.  Who designed this thing anyway?  So I couldn’t use the lookup class that acts_as_importable uses.  Bummer!  That’s ok, I just used the Rails stuff to see if it was defined to get the new users ID.

The last thing there is the Legacy::Importer.  That was where we do all the importing.  Tim’s blog does a good job explaining that.

step 4: add some columns to the tables.

This filled in the entries so that the foreign key look up stuff would work.

step 5: Set up rake task

This was tricky for me too.  I ended up creating vendor/plugins/acts-as-importable/tasks/acts_as_importable_tasks.rake.  The contents are as follows:

Step 6: Ok, now do it!… oh no…

To import the data, we run:

But this kept giving me som AMC errors…

step7: Fix/modify the plugin

After reading up more on plugins and all that jazz I dared change the code.  I changed the file /vendor/plugin/acts-as-importable/lib/acts_as_importable.rb. There was a reference to the AMC class (which was the origninal author’s project.).  So I removed that so that from the acts_as_importable method.  It now looks like this:

(Notice I simply copied the 2 lines from above and removed the AMC:: portion from them.  That seemed to get me further.

Step 8: Run it again!

After all that, it finally happened.  It was incredible!  Thanks so much Tim!  All the tables were pulled and the new site took on all the attributes of the old site.   But now I wonder what I did wrong or if there were easier steps I could have taken.  Probably, but it worked, and that’s the important part.  I launched the new site, patted myself on the back and went to bed.  Hope this helps you migrate data!

Comments are closed.