Schema Migrations with CakePHP 3
I corrected a few issues with the previous post regarding the redirect and
requirePost
event. They’ve been corrected.
In our previous post, I gave you all some homework:
- Make the form actions optional - and turn them off for embedded forms.
- Create a nicer comment list than the current version.
- Hide the
issue_id
field on the form without removing it completely
For the first task, you can extract the form actions into a separate element and make the inclusion of that element conditional - default true - on a variable you specify from the event.
For the second task, using a custom element for related entities is the way to go.
For the third task, you will want to populate a new variable - lets call it $inputOptions
- and have it be an array of field
=> options
for the field. Each field being output should be in this array with a default empty array as it’s options. You can use the BakeHelper::stringifyList
to turn those options into a nicely formatted string array.
One thing that is a pain is managing database schema changes. While we had CakeDC/migrations in the 2.x world, CakePHP 3 is about embracing existing solutions to problems. In CakePHP 3, we’ve delegated the task to the excellent Phinx library. Phinx is a database migration tool that CakePHP provides a wrapper for with the CakePHP/migrations plugin. You can use Phinx outside of CakePHP as well, so switching back and forth between CakePHP and other PHP frameworks should be a breeze.
To install the migrations plugin, we’ll use composer:
# ssh onto the vm
vagrant ssh
cd /vagrant/app
composer require cakephp/migrations:dev-master
At this point both phinx and the plugin will be installed. Plugins in CakePHP must be enabled before they can be used, and the CakePHP/migrations plugin is no different. Since it’s only useful on the command-line, we’ll enable it with the following code on our app/config/bootstrap_cli.php
:
<?php
use Cake\Core\Plugin;
Plugin::load('Migrations');
?>
Now that it’s enabled, we can generate our initial migration from the existing database:
cd /vagrant/app
bin/cake bake migration Initial
The output should be similar to the following:
Welcome to CakePHP v3.0.0-beta3 Console
---------------------------------------------------------------
App : src
Path: /vagrant/app/src/
---------------------------------------------------------------
Baking migration class for Connection default
Creating file /vagrant/app/config/Migrations/20141204225440_initial.php
Wrote `/vagrant/app/config/Migrations/20141204225440_initial.php`
If we look at that file, we’ll see a phinx-style migration that contains all the information about our current database schema. This can be useful for bootstrapping a new database (though our database works just fine for now). It’s pretty similar to the old migrations plugin - you get an up
, down
, and change
method - but uses an object-oriented approach to changing the database.
You can rollback any migration with the down()
callback by running the following command:
bin/cake migrations rollback
And if you have created new migrations, you can migrate up to them:
bin/cake migrations migrate
One note, there is currently an issue where the Phinx library auto-includes an auto-increment id
field for every database. This might not be desired for certain tables, in which case you’ll want to manually disable the field:
<?php
$table = $this->table('statuses', [
'id' => false,
'primary_key' => ['id']
]);
?>
For more docs, see the phinx documentation here
Homework Time!
This was a relatively short introduction to database migrations, but I felt it important enough to cover as we’ll be using them extensively over the next few tutorials. Your homework is actually pretty simple. We need to keep track of a webhook_url
string field with a length of 256 characters in our comments
table. Create a new migration and add the field to the table. The command to create an empty migration is as follows:
bin/cake migrations create WebhookUrl
Note, there is a bug in Phinx’s - not Cake’s! - templates where the end-docblock for the change()
method is in the wrong place. We’ll get that fixed up before CakePHP 3 goes stable :)