Migrations - the CakePHP way(?)
A quite popular feature in Rails are migrations, a kind of version control system for database schemes. Inspired by this feature, Joel Moss started a while ago the CakePHP migrations project to bring this feature to the CakePHP world.
In the meantime there is also a migrations-like feature in the Cake core, even though it seems to be a bit half-baked (or maybe I’m too stupid to understand it *g*).
Anyway, let’s have a look at it by using the traditional blog example.
First, we create a file which will contain the definitions for our schema. We name the file blog.php and place it in app/config/sql. In this file we create a class BlogSchema:
class BlogSchema extends CakeSchema {
}
Each property of a schema class represents a table, i.e. the property name is the table name, and the property value contains the table definition as an array. The definition for our posts table looks like:
class BlogSchema extends CakeSchema {
public $posts = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
'title' => array('type' => 'string', 'null' => false, 'length' => 100),
'content' => array('type' => 'text', 'null' => false),
'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
);
}
Now, we can switch to the command line and make a test run to verify the schema script will execute the correct SQL statements:
cd cake/console
./cake schema run create blog -dry
---------------------------------------------------------------
Cake Schema Shell
---------------------------------------------------------------
Performing a dry run.
The following tables will drop.
posts
Are you sure you want to drop the tables? (y/n)
[n] > y
Dropping tables.
Dry run for posts :
DROP TABLE IF EXISTS `posts`;
The following tables will create.
posts
Are you sure you want to create the tables? (y/n)
[y] > y
Creating tables.
Dry run for posts :
CREATE TABLE `posts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(100) NOT NULL,
`content` text NOT NULL,
PRIMARY KEY (`id`)
);
End create.
Everything looks fine, so we can run the same command again, this time without the -dry parameter, and it should create the posts table.
Ok, now let’s add a new column “created” to our schema:
class BlogSchema extends CakeSchema {
public $posts = array(
'id' => array('type' => 'integer', 'null' => false, 'default' => NULL, 'key' => 'primary'),
'title' => array('type' => 'string', 'null' => false, 'length' => 100),
'content' => array('type' => 'text', 'null' => false),
'created' => array('type' => 'datetime'),
'indexes' => array('PRIMARY' => array('column' => 'id', 'unique' => 1))
);
}
To introduce the new column we could use the command from above, which drops the table and recreates it. Or we could use the update command, which performs an “ALTER TABLE” statement to synchronize the posts table in the database with the posts table from the schema file:
./cake schema run update blog
---------------------------------------------------------------
Cake Schema Shell
---------------------------------------------------------------
Comparing Database to Schema...
The following statements will run.
ALTER TABLE `posts`
ADD `created` datetime DEFAULT NULL;
Are you sure you want to alter the tables? (y/n)
[n] > y
Updating Database…
posts updated.
End update.
With that, our blog database is up-to-date.
Unfortunately, the update command doesn’t work if you add a new table to the schema file, it doesn’t create this new table in the database. And so I think the migration process is currently not that useful in practice…
I used a question mark in the title of this article, as I am not sure if what I described here is really the correct way to use the schema feature. Please correct me, if I am wrong :)




I usually use them this way:
1. Update the tables in SQL (I really like the shell).
2. Create/Update models (This is what you are missing for the new added tables to work).
3. Update schema using cake shell
4. Commit to SCM (We use GIT ;-)
5. Tell other developers: “Hey guys, checkout (pull) my branch and update your schemas (using cake shell).”
6. Everybody is on sync and happy!
This is the workflow we use and is working for us.
P.S.: I found your blog very useful and visually appealing.
Daniel, thanks for this writeup about managing schemas in Cake. I’ve found this to be one of my issues with CakePHP, and I never really found any good documentation on it (but then again, I may just be looking in the wrong places).
It will be very useful if the schema is generated via bake script first. because database with 100+ tables for example will take forever to create this class :)
So far I am using good old phpMyAdmin where I wipe out all tables and import them all. I had few times where I need to update live database and then I just manually created the import sql and put it into a file.
Anyway, it’s good to know about this feature.
Cheers Daniel.
Hi Daniel, it looks like you’re really missing out on how the Cake schema tools were designed to be used. Dardo’s approach, noted above, is generally the correct way to go about things.
The fact is, as opposed to being “half-baked,” Cake’s schema tools are actually superior to Rails migrations, in that they solve several problems which Rails migrations create, and they’re more efficient for larger teams. They allow you to edit your databases in whatever tool you choose, and they leverage existing version control systems to prevent conflicts when schemas are being edited by multiple people in parallel, which is a common problem with Rails migrations.
@all: Thanks for your comments!
@dardo: I’m a bit surprised that your approach works ;-) If I create a model for a table which doesn’t exist yet, and try to run the update script (step 5 in your approach), then the script fails with a “missing database table” error…
@Kyle: I think there is no documentation except the help text you get when you call:
@Nik: For existing databases you can use the following command to generate the schema file:
@nate: I can’t say whether Cake’s approach is superior to Rails’ migrations. But if I can’t figure out how to use it resp. if it doesn’t work, then it doesn’t matter that Cake’s concept may be superior…
Hey Daniel,
You’ve hit the nail on the head in regards to your comments to Nate.
If the code matched the doc’s, everything would be perfect.
It would be great if there was a complete tutorial/documentation about the correct workflow for this somewhere.
Reading Dardo’s comment, I still don’t understand the correct steps (ok maybe I’m not so smart).
It would be great if there was a complete tutorial/documentation about the correct workflow for this somewhere.
Reading Dardo’s comment, I still don’t understand the correct steps (ok maybe I’m too stupid).
@Beth: Yes, the documentation for this feature could be better :)
@chanon: You are not alone ;-) And yes, it would be cool to have the correct workflow documented.
Hey Daniel and “Bakers”,
well I while ago (November 2007) I’ve developed a shell that autogenerates (in yaml format) the DB schemas on the fly, ready to be used by the migrations a-la-Joel-Moss.
If you are interested you can download the shell and discover more in this article: YAMMY! DB to Yaml shell: migrations made easy in CakePHP
@Daniel: Thanks for the link! Isn’t your script included in the CakePHP migrations, is it?
[...] [1] http://cakebaker.42dh.com/2008/04/13/migrations-the-cakephp-way/ [...]
@cakebaker, @beth, @chanon: Have any of you ever tried typing “cake schema create”? It’s not exactly rocket science.
@cakebaker: I’m not asking you if it’s superior, I’m telling you.
Oops, should have been “cake schema help”.
@nate: I know that command, I used it while writing this article…
My two cents (for what it’s worth, which is just a little shy of two cents):
CakePHP is an open-source, community-driven project. To make a blog post about how to use a feature precludes one from complaining about a lack of documentation.
If the amount of energy used to write an article in an arbitrary location was instead used to submit the exact same information to the documentation efforts, then the complaints about ‘lack of documentation’ would simply go away. The cry for documentation would be heard by exactly one person and then never heard again.
@TommyO: In principle, you are right. But personally, I decided consciously against supporting the documentation efforts for several reasons. And in the end, I think it doesn’t matter, where the documentation is located, as with search engines it can be found anywhere.